Soporte de C ++ 11 para funciones de lista de orden superior

13

La mayoría de los lenguajes de programación funcionales (por ejemplo, Common Lisp, Scheme / raqueta, Clojure, Haskell, Scala, Ocaml, SML) soportan algunas funciones comunes de orden superior en las listas, como por ejemplo map, filter, takeWhile, dropWhile, foldl, foldr(véase, por ejemplo Common Lisp, Scheme / raqueta, Hoja de referencia de lado a lado de Clojure , la documentación de Haskell , Scala , OCaml y SML ).

¿C ++ 11 tiene métodos o funciones estándar equivalentes en las listas? Por ejemplo, considere el siguiente fragmento de Haskell:

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

¿Cómo puedo expresar la segunda expresión en el estándar moderno C ++?

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

¿Qué pasa con las otras funciones de orden superior mencionadas anteriormente?
¿Se pueden expresar directamente en C ++?

Giorgio
fuente
Sí, pero operan con conceptos más generales que esa implementación específica de una lista doblemente vinculada. Al igual que las operaciones de Python en esta área. Prefiero eso a estar atado a una estructura de datos específica. ¿Alguna vez trató de hacer estas operaciones en, digamos, un Data.SequenceHaskell? Es comparativamente feo.
"Es comparativamente feo": ¿Comparado con qué?
Giorgio
En comparación con la misma operación en [a]. Debe ocultar la función de preludio, piratear el preludio o elegir un nombre diferente y menos intuitivo.
Quizás tenga razón, pero el tema de esta pregunta es cómo expresar funciones comunes de orden superior de lista en C ++, no cómo implementar funciones análogas en Data.Sequence en Haskell.
Giorgio
1
@delnan Yo diría que Haskell es mucho más general en su enfoque. Functor, Foldabley Traversablelograr esto de una manera tan abstracta como puedo pensar. Data.Sequencees una instancia de todos estos, por lo que puede hacer fmap (\x -> x * x) xs. mapEstá fmapespecializado para principiantes.
Alec

Respuestas:

16

Aún más, C ++ tiene tales funciones, eche un vistazo al encabezado del algoritmo (o con adiciones de C ++ 11 ):

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

Se pueden usar fácilmente con cualquier contenedor.

Por ejemplo, su código puede expresarse así (con C ++ 11 lambdas para una fácil codificación):

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

Menos intuitivo, pero puede ajustar fácilmente la std::transformllamada a una función que devolvería un nuevo contenedor (con movesemántica para un mejor rendimiento).

m0nhawk
fuente
Gracias. Me gustaría simplificar un código que escribí hace unos días y esto realmente podría ayudar a que sea mucho más corto. Solo una pequeña pregunta: ¿por qué necesita pasar x.begin () y x.end ()? ¿No sería suficiente simplemente pasar el vector x?
Giorgio
std::transformtoma dos iteradores, por lo tanto, puede tomar una rebanada de un contenedor (recuerde que tiene iteradores aritméticos).
m0nhawk
Entonces tiene dos operaciones en una: tomar un corte y aplicar una transformación.
Giorgio
Anteriormente tenía dos iteradores y aplicaba transformación a elementos entre ellos. Los iteradores no son comunes en la programación funcional.
m0nhawk
2
No he conocido tales bibliotecas, en C ++ el algoritmo basado en iterador es muy útil. Se puede hacer una envoltura de, en su caso, std::transformcomo: Y<U> map(T<U>, std::function<Y(U)>).
m0nhawk el