2012-04-04 26 views
1

Ich war ein fast ausschließlich C# -Programmierer für die letzten 6 Jahre. Ich arbeite jetzt an einem Projekt, in dem C++ die Sprache der Wahl ist, und STL ist unsere Bibliothek für Sammlungen.Tipps für die Annahme eines STL-Geistes

Nachdem ich C# 's LINQ verwendet habe, fällt es mir sehr schwer, in eine STL-Umgebung einzusteigen.

Als Beispiel: Schreiben Sie ein Äquivalent von IEnumerator.Select.

C#

public static IEnumerator<Output> Select(this IEnumerator<Input> input, Func<Input, Output> func) { 
    while (input.MoveNext) { 
    yield return func(input.Current); 
    } 
} 

Super einfach.

Jetzt versuchen, etwas ähnliches in C++ und STL zu schreiben. (Die Frage der bequemen Syntax des yield-Schlüsselworts und der anonymen Funktionen beiseite lassen).

Es kann nicht einmal getan werden, ohne zuerst eine Reihe von schwierigen Fragen zu beantworten. Da STL-Enumeratoren Inter-Enumerator-Vergleiche anstelle von MoveNext verwenden, müssen Sie entscheiden, wie hoch der Endwert Ihres Enumerators sein soll. Dann musst du dich mit all dem iterator_traits Unsinn herumschlagen. STL verwendet den Vorlagenversand der Kompilierungszeit anstelle des dynamischen Dispatchs der Laufzeit. Daher muss Ihr Iterator nicht nur auf den Werttyp des Eingabeenumerators, sondern auch auf die spezielle Art des Eingabeenumerators festgelegt sein.

Erhalte mich nicht einmal zu der Zeit, als ich versuchte, einen Map-Join-Iterator in STL zu schreiben.

Aus dem Blick auf den Code, den andere Leute schreiben, bin ich zu dem Schluss gekommen, dass STL, unverstärkt mit Boost, selten für etwas anderes als Sammlungen und Sortieren verwendet wird.

Meine proximalen Beobachtungen sind:

  1. Gibt es eine Möglichkeit, um kurz und bündig eine Mutation Iterator in STL zu schreiben?
  2. Wie summieren Sie eine Sammlung summarisch in eine andere?

Allgemeiner gesagt, ich habe ein paar Dinge aufgefallen, die mit meiner gewohnten Denkweise kollidieren:

  1. AWL-Code nicht prägnant zu sein scheint. Ist es mein Ziel, prägnanten Code beim Schreiben von STL-Code zu schreiben? (Durch nicht kurz, ich meine, oft beinhaltet sehr lange Typ-IDs)
  2. Boost scheint eine fast Voraussetzung für das Schreiben von Algorithmen in STL sein. Was machen Leute, die Boost nicht benutzen dürfen?
+0

Nein, die Ströme sind gebrochen, aber der Rest ist in Ordnung. Boost und QT erweitern beide die STL in viel größere Frameworks, Boost als allgemeine Erweiterung, QT für GUI-bezogene Sachen. Wenn ich 'IEnumerator.Select' richtig verstehe, nennen wir das' boost :: transform_iterator'. Wenn ich "boost :: zip_iterator" verstehe, ist das der Map-Join-Iterator. –

+0

Ich sollte beachten, dass meine Projektanforderungen Boost verbieten. –

+0

Sie sollten einen Blick darauf werfen, was der '' Header bietet – Attila

Antwort

2

Einige Ihrer Frage macht nicht wirklich viel Sinn für mich. Zum Beispiel reden Sie über "enumerator_traits". Ich bin mir nicht ganz sicher, wovon du redest. Vielleicht meintest du iterator_traits? Ich kann mich nicht daran erinnern, irgend etwas namens "enumerator_trait" benutzt zu haben, noch kann ich eine solche Sache im C++ - Standard finden.

iterator_traits zumindest existieren, aber sie sind etwas, das ich selten "durcheinander". Ich bin peripher ihrer Existenz bewusst, aber wenig mehr als das. Ich habe eine ganze Reihe von Iteratoren und Algorithmen geschrieben, ohne jemals etwas Bestimmtes mit iterator_traits zu tun.

Kommen wir zur spezifischen Frage der Erstellung einer neuen Sammlung, die eine sortierte Version einer anderen Sammlung ist. Dies ist in vielerlei Hinsicht ziemlich einfach. std::partial_sort_copy kann sicherlich tun:

#include <algorithm> 
#include <vector> 
#include <iterator> 
#include <iostream> 

int main() { 

    std::vector<int> input; 

    // generate some data to sort 
    std::generate_n(std::back_inserter(input), 20, rand); 

    // a destination for the sorted data: 
    std::vector<int> result(input.size()); 

    // do the sort/copy: 
    std::partial_sort_copy(input.begin(), input.end(), 
     result.begin(), result.end()); 

    // show the sorted data: 
    std::copy(result.begin(), result.end(), 
     std::ostream_iterator<int>(std::cout, "\n")); 

    return 0; 
} 

Für viele Zwecke ist es jedoch einfacher, eine Kopie zu machen, dann sort:

std::vector<int> result(input.begin(), input.end()); 
std::sort(result.begin(), result.end()); 

Wenn Sie es wirklich prägnante möchten, können Sie eine Kopie in einem machen kann Datenstruktur, die angeboren sortiert ist:

std::multiset<int> result(input.begin(), input.end()); 

Letzteres jedoch handelt in der Regel eine wenig Effizienz aus dem Code kürzer zu machen. Unter vielen (meisten?) Umständen ist das kein Problem, aber wenn Sie es zu langsam finden, sind schnellere Alternativen leicht verfügbar.

+0

Hier ist eine Art mit nur einer einzigen Kopie: http://ideone.com/s7IjE. 21 Zeilen. Der längste Bezeichner, den ich eingegeben habe, war 'std :: vector '. –

+0

und ein Transformations-Iterator: http://ideone.com/PYyvY. 26 Zeilen. Komplizierter, immer noch nicht so schlimm. Die normale Sache ist, wie Jerry sagt, nur "std :: transform" sie alle auf einmal. –

+0

@Mooing Duck Es gibt eine wichtige, nicht offensichtliche, allgemein anwendbare Offenbarung in Ihrer Implementierung des Transformations-Iterators. Das Aufrufen von make_transform auf dem Endpunkt-Iterator ist für einen LINQ-Programmierer nicht offensichtlich und scheint ein guter Ansatz zu sein. –

Verwandte Themen