Prise en charge de C ++ 11 pour les fonctions de liste d'ordre supérieur

13

La plupart des langages de programmation fonctionnelle (par exemple , en Common Lisp, Scheme / Racket, Clojure, Haskell, Scala, Ocaml, SML) prennent en charge certaines fonctions communes d'ordre supérieur sur les listes, telles que map, filter, takeWhile, dropWhile, foldl, foldr(voir par exemple Common Lisp, Scheme / Racket, Clojure côte à côte , la feuille de référence , la documentation Haskell , Scala , OCaml et SML .)

C ++ 11 a-t-il des méthodes ou fonctions standard équivalentes sur les listes? Par exemple, considérez l'extrait de code Haskell suivant:

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

Comment puis-je exprimer la deuxième expression en C ++ standard moderne?

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

Qu'en est-il des autres fonctions d'ordre supérieur mentionnées ci-dessus?
Peut-on les exprimer directement en C ++?

Giorgio
la source
Oui, mais ils fonctionnent sur des concepts plus généraux que cette mise en œuvre spécifique d'une liste doublement liée. Tout comme les opérations de Python dans ce domaine. Je préfère de loin que d'être lié à une structure de données spécifique. Avez-vous déjà essayé de faire ces opérations sur, disons, Data.Sequenceà Haskell? C'est relativement moche.
"C'est relativement moche.": Par rapport à quoi?
Giorgio
Par rapport à la même opération sur [a]. Vous devez soit masquer la fonction prélude, pirater le prélude, soit choisir un nom différent et moins intuitif.
Vous avez peut-être raison, mais le sujet de cette question est de savoir comment exprimer des fonctions communes d'ordre supérieur en C ++, pas comment implémenter des fonctions analogues sur Data.Sequence dans Haskell.
Giorgio
1
@delnan Je dirais que Haskell est beaucoup plus général dans son approche. Functor, FoldableEt y Traversableparvenir d' une manière aussi abstraite que d' un je peux penser. Data.Sequenceest une instance de tout cela, vous pouvez donc le faire fmap (\x -> x * x) xs. mapest fmapspécialisé pour les débutants.
Alec

Réponses:

16

De plus, C ++ a de telles fonctions, jetez un œil à l' en- tête de l' algorithme (ou avec des ajouts C ++ 11 ):

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

Ils peuvent être facilement utilisés avec n'importe quel conteneur.

Par exemple, votre code peut être exprimé comme ceci (avec des lambdas C ++ 11 pour un codage facile):

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; });

Moins intuitif, mais vous pouvez facilement envelopper l' std::transformappel dans une fonction qui retournerait un nouveau conteneur (avec de la movesémantique pour de meilleures performances).

m0nhawk
la source
Merci. Je voudrais simplifier du code que j'ai écrit il y a quelques jours et cela pourrait vraiment aider à le raccourcir. Juste une petite question: pourquoi avez-vous besoin de passer x.begin () et x.end ()? Ne serait-il pas suffisant de simplement passer le vecteur x?
Giorgio
std::transformprend deux itérateurs, vous pouvez donc prendre une tranche de récipient (rappelez-vous que vous avez des arithmétiques itérateurs).
m0nhawk
Vous avez donc deux opérations en une: prendre une tranche et appliquer une transformation.
Giorgio
Auparavant, vous avez deux itérateurs et appliquez une transformation aux éléments entre eux. Les itérateurs ne sont pas courants dans la programmation fonctionnelle.
m0nhawk
2
Je n'ai pas rencontré de telles bibliothèques, en C ++ l'algorithme basé sur un itérateur est très utile. Vous pouvez faire un emballage de, dans votre cas, std::transformcomme: Y<U> map(T<U>, std::function<Y(U)>).
m0nhawk