Поддержка C ++ 11 для функций списка высшего порядка

13

Большинство функциональных языков программирования (например , Common Lisp, Scheme / ракетки, Clojure, Haskell, Scala, Ocaml, SML) поддерживают некоторые общие функции высшего порядка в списках, такие как map, filter, takeWhile, dropWhile, foldl, foldr(см , например , Common Lisp, Scheme / Ракетка, Clojure бок о бок справочный лист , Haskell , Scala , OCaml и документацию SML .)

Есть ли в C ++ 11 эквивалентные стандартные методы или функции в списках? Например, рассмотрим следующий фрагмент Haskell:

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

Как я могу выразить второе выражение в современном стандарте C ++?

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

Как насчет других функций высшего порядка, упомянутых выше?
Могут ли они быть прямо выражены в C ++?

Джорджио
источник
Да, но они оперируют более общими понятиями, чем конкретная реализация двусвязного списка. Как и операции Python в этой области. Я предпочитаю это привязке к определенной структуре данных. Когда-нибудь пытались сделать эти операции, скажем, Data.Sequenceв Haskell? Это сравнительно безобразно.
«Это сравнительно некрасиво». По сравнению с чем?
Джорджио
По сравнению с той же операцией на [a]. Вы должны либо скрыть функцию prelude, взломать prelude, либо выбрать другое и менее интуитивное имя.
Может быть, вы и правы, но тема этого вопроса - как выразить общие функции высшего порядка в C ++, а не как реализовать аналогичные функции в Data.Sequence в Haskell.
Джорджио
1
@delnan Я бы сказал, что Haskell гораздо более общий подход. Functor, FoldableИ Traversableдобиться этого как абстрактно, как я могу думать. Data.Sequenceявляется примером всего этого, так что вы можете просто сделать fmap (\x -> x * x) xs. mapэто fmapспециализирован для начинающих.
Алек

Ответы:

16

Более того, в C ++ есть такие функции, взгляните на заголовок алгоритма (или с добавлением C ++ 11 ):

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

Их можно легко использовать с любым контейнером.

Например, ваш код может быть выражен следующим образом (с лямбдами C ++ 11 для простого кодирования):

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

Менее интуитивно, но вы можете легко обернуть std::transform вызов в функцию, которая вернет новый контейнер (с moveсемантикой для лучшей производительности).

m0nhawk
источник
Благодарю. Я хотел бы упростить некоторый код, который я написал несколько дней назад, и это действительно может сделать его намного короче. Один маленький вопрос: зачем вам передавать x.begin () и x.end ()? Не будет достаточно просто передать вектор х?
Джорджио
std::transformзанимает два итератора, так что вы можете взять кусок контейнера (помните, что у вас есть арифметика итераторов).
m0nhawk
Таким образом, у вас есть две операции в одной: взятие среза и применение преобразования.
Джорджио
Раньше у вас было два итератора и применение преобразования к элементам между ними. Итераторы не распространены в функциональном программировании.
m0nhawk
2
Я не встречал таких библиотек, в C ++ алгоритм, основанный на итераторах, очень полезен. Вы можете сделать обертку, в вашем случае, std::transformкак: Y<U> map(T<U>, std::function<Y(U)>).
m0nhawk