C ++ 11-Unterstützung für Listenfunktionen höherer Ordnung

13

Die meisten funktionalen Programmiersprachen (zB Common Lisp, Scheme / Schläger, Clojure, Haskell, Scala, Ocaml, SML) unterstützen einige gemeinsame Funktionen höherer Ordnung auf Listen, wie map, filter, takeWhile, dropWhile, foldl, foldr(siehe zB Common Lisp, Scheme / Schläger, Clojure Seite an Seite Referenzblatt , die Haskell- , Scala- , OCaml- und die SML- Dokumentation.)

Verfügt C ++ 11 über gleichwertige Standardmethoden oder -funktionen für Listen? Betrachten Sie beispielsweise das folgende Haskell-Snippet:

let xs = [1, 2, 3, 4, 5]
let ys = map (\x -> x * x) xs

Wie kann ich den zweiten Ausdruck in modernem Standard-C ++ ausdrücken?

std::list<int> xs = ... // Initialize the list in some way.
std::list<int> ys = ??? // How to translate the Haskell expression?

Was ist mit den anderen Funktionen höherer Ordnung, die oben erwähnt wurden?
Können sie direkt in C ++ ausgedrückt werden?

Giorgio
quelle
Ja, aber sie arbeiten mit allgemeineren Konzepten als der spezifischen Implementierung einer doppelt verknüpften Liste. Wie Pythons Operationen in diesem Bereich. Ich bevorzuge es sehr, an eine bestimmte Datenstruktur gebunden zu sein. Haben Sie jemals versucht, diese Operationen beispielsweise Data.Sequencein Haskell durchzuführen ? Es ist vergleichsweise hässlich.
"Es ist vergleichsweise hässlich.": Im Vergleich zu was?
Giorgio
Im Vergleich zum gleichen Vorgang auf [a]. Sie müssen entweder die Vorspielfunktion ausblenden, das Vorspiel durchgehen oder einen anderen und weniger intuitiven Namen wählen.
Vielleicht haben Sie Recht, aber in dieser Frage geht es darum, wie häufig verwendete Funktionen höherer Ordnung in C ++ ausgedrückt werden, und nicht wie analoge Funktionen in Data.Sequence in Haskell implementiert werden.
Giorgio
1
@delnan Ich würde argumentieren, dass Haskell in seiner Herangehensweise viel allgemeiner ist . Functor, Foldableund Traversabledies auf eine so abstrakte Art und Weise zu erreichen, wie ich denken kann. Data.Sequenceist eine Instanz von all diesen, so können Sie einfach tun fmap (\x -> x * x) xs. mapist fmapspezialisiert für Anfänger.
Alec

Antworten:

16

Noch mehr, C ++ hat solche Funktionen, werfen Sie einen Blick auf den Header des Algorithmus (oder mit C ++ 11-Zusätzen ):

std::transform
std::for_each
std::remove_copy_if

Sie können problemlos mit jedem Behälter verwendet werden.

Zum Beispiel kann Ihr Code so ausgedrückt werden (mit C ++ 11 Lambdas zur einfachen Codierung):

std::vector<int> x = {1, 2, 3, 4, 5};
std::vector<int> y;
std::transform(x.begin(), x.end(), std::back_inserter(y), [](int elem){ return elem * elem; });

Weniger intuitiv, aber Sie können den std::transformAufruf leicht in eine Funktion moveumbrechen, die einen neuen Container zurückgibt (mit Semantik für eine bessere Leistung).

m0nhawk
quelle
Vielen Dank. Ich würde gerne einen Code vereinfachen, den ich vor ein paar Tagen geschrieben habe, und das könnte wirklich helfen, ihn viel kürzer zu machen. Nur eine kleine Frage: Warum müssen Sie x.begin () und x.end () übergeben? Wäre es nicht ausreichend, nur den Vektor x zu übergeben?
Giorgio
std::transformdauert zwei Iteratoren, so können Sie ein Stück eines Containers nehmen (denken Sie daran, dass Sie Iteratoren Arithmetik haben).
m0nhawk
Sie haben also zwei Operationen in einer: Ein Slice nehmen und eine Transformation anwenden.
Giorgio
Früher haben Sie zwei Iteratoren und wenden die Transformation auf Elemente zwischen ihnen an. Iteratoren sind in der funktionalen Programmierung nicht weit verbreitet.
m0nhawk
2
Ich habe solche Bibliotheken nicht getroffen, in C ++ ist der auf Iteratoren basierende Algorithmus sehr nützlich. Sie können einen Wrapper, in Ihrem Fall machen, std::transformwie: Y<U> map(T<U>, std::function<Y(U)>).
m0nhawk