Obsługa C ++ 11 dla funkcji list wyższych

13

Większość języków programowania funkcyjnego (np Common Lisp, Scheme / rakieta, Clojure, Haskell, Scala, Ocaml, SML) obsługuje niektórych funkcji wspólnego wyższego rzędu na listach, takich jak map, filter, takeWhile, dropWhile, foldl, foldr(patrz np Common Lisp, Scheme / rakieta, Clojure obok siebie dokumentacja , dokumentacja Haskell , Scala , OCaml i SML ).

Czy C ++ 11 ma równoważne standardowe metody lub funkcje na listach? Weźmy na przykład następujący fragment kodu Haskell:

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

Jak mogę wyrazić drugie wyrażenie we współczesnym standardowym języku C ++?

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

Co z innymi funkcjami wyższego rzędu wymienionymi powyżej?
Czy można je bezpośrednio wyrazić w C ++?

Giorgio
źródło
Tak, ale działają na bardziej ogólnych pojęciach niż ta konkretna implementacja podwójnie powiązanej listy. Podobnie jak operacje Pythona w tym obszarze. Zdecydowanie wolę to od powiązania z określoną strukturą danych. Czy kiedykolwiek próbowałeś wykonywać te operacje, powiedzmy, Data.Sequencew Haskell? Jest stosunkowo brzydki.
„Jest stosunkowo brzydki.”: W porównaniu do czego?
Giorgio
W porównaniu do tej samej operacji na [a]. Musisz ukryć funkcję preludium, zhakować preludium lub wybrać inną i mniej intuicyjną nazwę.
Być może masz rację, ale tematem tego pytania jest, jak wyrazić wspólne funkcje wyższego rzędu w C ++, a nie jak zaimplementować analogiczne funkcje w Data.Sequence w Haskell.
Giorgio
1
@delnan Twierdziłbym, że Haskell jest znacznie bardziej ogólny w swoim podejściu. Functor, Foldablei Traversableosiągnę to w sposób tak abstrakcyjny, jak tylko potrafię. Data.Sequencejest ich przykładem, więc możesz to zrobić fmap (\x -> x * x) xs. mapjest fmapspecjalistyczne dla początkujących.
Alec

Odpowiedzi:

16

Co więcej, C ++ mają takie funkcje, spójrz na nagłówek algorytmu (lub z dodatkami C ++ 11 ):

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

Można je łatwo stosować z dowolnym pojemnikiem.

Na przykład twój kod może być wyrażony w następujący sposób (z lambda C ++ 11 dla łatwego kodowania):

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

Mniej intuicyjny, ale z łatwością można zawinąć std::transformwywołanie w funkcję, która zwróciłaby nowy kontener (z movesemantyką dla lepszej wydajności).

m0nhawk
źródło
Dzięki. Chciałbym uprościć kod napisany kilka dni temu, a to może naprawdę pomóc w skróceniu go. Tylko jedno małe pytanie: dlaczego musisz przekazać x.begin () i x.end ()? Nie wystarczyłoby tylko przekazać wektor x?
Giorgio
std::transformzajmuje dwa iteratory, więc możesz wziąć kawałek kontenera (pamiętaj, że masz arytmetykę iteratorów).
m0nhawk
Tak więc masz dwie operacje w jednym: pobranie wycinka i zastosowanie transformacji.
Giorgio
Dawniej masz dwa iteratory i stosujesz transformację do elementów między nimi. Iteratory nie są powszechne w programowaniu funkcjonalnym.
m0nhawk
2
Nie spotkałem takich bibliotek, w C ++ algorytm iteracyjny jest bardzo przydatny. Można zrobić z opakowania, w danym przypadku, std::transformjak: Y<U> map(T<U>, std::function<Y(U)>).
m0nhawk