Jestem nowy w C ++ 11. Piszę następującą rekurencyjną funkcję lambda, ale nie kompiluje się.
sum.cpp
#include <iostream>
#include <functional>
auto term = [](int a)->int {
return a*a;
};
auto next = [](int a)->int {
return ++a;
};
auto sum = [term,next,&sum](int a, int b)mutable ->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
int main(){
std::cout<<sum(1,10)<<std::endl;
return 0;
}
błąd kompilacji:
vimal @ linux-718q: ~ / Study / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp
sum.cpp: W funkcji lambda: sum.cpp: 18: 36: błąd: „ ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum
” nie można użyć jako funkcji
wersja gcc
gcc wersja 4.5.0 20091231 (eksperymentalna) (GCC)
Ale jeśli zmienię deklarację sum()
jak poniżej to działa:
std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
Czy ktoś mógłby rzucić na to światło?
mutable
tam robi słowo kluczowe?std::function<int(int,int)> sum = [&](int a, int b) {
Odpowiedzi:
Pomyśl o różnicy między wersją automatyczną a pełną wersją typu. Te auto wyprowadza słów kluczowych jego typ z cokolwiek to jest inicjowany, ale co ty inicjowanie go musi wiedzieć, co jest jego typ (w tym przypadku potrzeby zamykania lambda znać typy to przechwytywanie). Coś w rodzaju problemu jajka i kury.
Z drugiej strony, w pełni określony typ obiektu funkcji nie musi „wiedzieć” o tym, co jest do niego przypisane, a więc zamknięcie lambdy może być również w pełni poinformowane o typach, które przechwytuje.
Rozważ tę niewielką modyfikację swojego kodu i może mieć więcej sensu:
Oczywiście to nie zadziała z auto . Rekurencyjne funkcje lambda działają doskonale (przynajmniej działają w MSVC, gdzie mam z nimi doświadczenie), po prostu nie są tak naprawdę zgodne z wnioskiem o typie.
źródło
auto
zmiennej w jej inicjatorze. typ zmiennej auto nie jest jeszcze znany, kiedy inicjalizator jest przetwarzany.sum
innego niżstd::function<int(int, int)>
, czy specyfikacja C ++ po prostu nie zadała sobie trudu, aby ją wywnioskować?Sztuczka polega na tym, aby przekazać implementację lambda do siebie jako parametr , a nie przez przechwycenie.
Wszystkie problemy w informatyce można rozwiązać na innym poziomie pośrednictwa . Po raz pierwszy znalazłem tę prostą sztuczkę na http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/
To nie wymaga C ++ 14, natomiast pytanie o C ++ 11, ale być może najbardziej interesujące.
Przechodzenie przez
std::function
jest również możliwe, ale może skutkować wolniejszym kodem. Ale nie zawsze. Spójrz na odpowiedzi na std :: function vs templateTo nie tylko osobliwość C ++, ale bezpośrednie odwzorowanie na matematykę rachunku lambda. Z Wikipedii :
źródło
function<>
. Nie rozumiem, dlaczego ktoś by to wolał. Edycja: najwyraźniej jest szybszy.error: use of ‘[...]’ before deduction of ‘auto’
- potrzebne do jawnego określenia typu zwracanego (z drugiej strony nie potrzebował mutowalności).W C ++ 14 jest teraz całkiem łatwo stworzyć wydajną rekurencyjną lambdę bez konieczności ponoszenia dodatkowego narzutu
std::function
, w zaledwie kilku wierszach kodu (z niewielką edycją oryginału, aby uniemożliwić użytkownikowi wykonanie przypadkowej kopii ):z którym twoja pierwotna
sum
próba staje się:W C ++ 17 z CTAD możemy dodać przewodnik po dedukcji:
Co eliminuje potrzebę funkcji pomocniczej. Możemy po prostu napisać
y_combinator{[](auto self, ...){...}}
bezpośrednio.W C ++ 20, z CTAD dla agregatów, przewodnik dedukcji nie będzie potrzebny.
źródło
std::forward<decltype(sum)>(sum)
zamiastsum
w ostatniej linii.operator()
więc nie ma nic do zyskania przez przekazywaniesum
const
przeciążenie na wypadek, gdyby dostarczony obiekt-funkcji nie byłconst
operatorem wywołania. I użyj SFINAE i oblicznoexcept
dla obu. Ponadto nie ma już potrzeby korzystania z funkcji maker w C ++ 17.auto sum
kopiuje ... ale kopiuje areference_wrapper
, co jest tym samym, co pobieranie referencji. Zrobienie tego raz podczas implementacji oznacza, że żadne użycie nie zostanie przypadkowo skopiowane.Mam inne rozwiązanie, ale pracuję tylko z bezstanowymi lambdami:
Sztuczka polega na tym, że lambdy mają dostęp do zmiennych statycznych i można przekonwertować je bezstanowe na wskaźnik funkcji.
Możesz go używać ze standardowymi lambdami:
Jego praca w GCC 4.7
źródło
Państwo może dokonać funkcja lambda nazywać się rekurencyjnie. Jedyne, co musisz zrobić, to odwołać się do niego za pomocą opakowania funkcji, aby kompilator wiedział, jaki jest jego zwrot i typ argumentu (nie możesz przechwycić zmiennej - samej lambdy - która nie została jeszcze zdefiniowana) .
Uważaj, aby nie zabraknąć opakowania opakowania f.
źródło
Aby uczynić lambdę rekurencyjną bez użycia zewnętrznych klas i funkcji (takich jak
std::function
lub kombinator stałoprzecinkowy), można użyć następującej konstrukcji w C ++ 14 ( przykład na żywo ):wydruki:
Uwaga: typ wyniku lambda powinien być jawnie określony.
źródło
Przeprowadziłem test porównawczy porównujący funkcję rekurencyjną z rekurencyjną funkcją lambda przy użyciu
std::function<>
metody przechwytywania. Z pełną optymalizacją włączoną w wersji Clang 4.1, wersja lambda działała znacznie wolniej.Daje wyniki:
(Uwaga: potwierdziłem również wersją, która pobierała dane wejściowe z cin, aby wyeliminować ocenę czasu kompilacji)
Clang generuje również ostrzeżenie kompilatora:
Co jest oczekiwane i bezpieczne, ale należy to zauważyć.
Wspaniale jest mieć rozwiązanie w naszych paskach narzędzi, ale myślę, że język będzie wymagał lepszego sposobu obsługi tego przypadku, jeśli wydajność ma być porównywalna z obecnymi metodami.
Uwaga:
Jak zauważył komentator, wydaje się, że najnowsza wersja VC ++ znalazła sposób na zoptymalizowanie tego do punktu równej wydajności. Może w końcu nie potrzebujemy lepszego sposobu, aby sobie z tym poradzić (z wyjątkiem cukru syntaktycznego).
Ponadto, jak wskazały inne posty SO w ostatnich tygodniach, wydajność
std::function<>
sama w sobie może być przyczyną spowolnienia w porównaniu do bezpośredniego wywoływania funkcji, przynajmniej gdy przechwytywanie lambda jest zbyt duże, aby zmieścić się w niektórych zoptymalizowanych bibliotekachstd::function
zastosowań przestrzeni dla małych funktorów (Myślę, że trochę lubię różne optymalizacje krótkich ciągów?).źródło
Jest to nieco prostsza implementacja operatora punktu stałego, dzięki czemu jest trochę bardziej oczywiste, co się dzieje.
źródło
std::function
wskaźnikiem funkcji (rdzeni będzie działać tylko z normalną funkcją i bezstanowymi lambdami). Btwfib_nonr
powinien zaakceptowaćfixpoint<int,int>
, jeśli używaszstd::function
jego wymaganego tworzenia nowej kopii z*this
.Oto udoskonalona wersja rozwiązania kombinatora Y oparta na rozwiązaniu zaproponowanym przez @Barry.
Aby z tego skorzystać, można wykonać następujące czynności
Jest podobne do
let rec
słowa kluczowego w OCaml, ale nie jest takie samo.źródło
C ++ 14: Oto rekurencyjny, anonimowy, bezstanowy / bez przechwytywania zestaw lambd, który wyświetla wszystkie liczby od 1, 20
Jeśli dobrze rozumiem, jest to rozwiązanie z kombinatorem Y
A oto wersja suma (n, m)
źródło
Oto ostateczna odpowiedź na PO. W każdym razie Visual Studio 2010 nie obsługuje przechwytywania zmiennych globalnych. I nie musisz ich przechwytywać, ponieważ zmienna globalna jest dostępna globalnie przez zdefiniowanie. Następująca odpowiedź używa zamiast tego zmiennej lokalnej.
źródło
Próbujesz uchwycić zmienną (sumę), którą właśnie definiujesz. To nie może być dobre.
Nie sądzę, aby możliwe były prawdziwie autorekurencyjne lambdy C ++ 0x. Powinieneś być jednak w stanie wychwycić inne lambdy.
źródło
Ta odpowiedź jest gorsza od odpowiedzi Yankesa, ale mimo to brzmi:
źródło
reinterpret_cast
. Prawdopodobnie najlepszym sposobem w twoim przypadku jest stworzenie struktury, która zastąpidp_type
. Powinien mieć polefp_type
, może być zbudowany zfp_type
i mieć operator()
z argumentami takimi jakfp_type
. Będzie to bliskie,std::function
ale pozwoli na argumentację odwołującą się do siebie.struct
dodałby również dodatkowy poziom pośredni. Przykład działa, a obsada jest zgodna ze standardami, nie wiem, do czego-1
służyła.-1
tym nie wiedziałem, kto ci go dał, ale myślę, że to dlatego, żereinterpret_cast
powinno być używane jako ostateczność.cast
podobno działać zgodnie ze standardem c ++ 11. Używaniestruct
, w moich oczach, mogła pokonać wykorzystanie obiektu lambda. W końcu to,struct
co proponujesz, jest funktorem wykorzystującym obiekt lambda.std::function
a będziesz miał coś zbliżonego do tego, o czym myślałem. To prawdopodobnie będzie miało podobną wydajność do twojego rozwiązania.Potrzebujesz kombinatora punktów stałych. Zobacz to .
lub spójrz na poniższy kod:
źródło