Hoe de volgende recursieve lambda-oproep eindigt/beëindigt?
#include <cstdio>
auto terminal = [](auto term) // <---------+
{ // |
return [=] (auto func) // | ???
{ // |
return terminal(func(term)); // >---------+
};
};
auto main() -> int
{
auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; };
auto world =[](auto s){ fprintf(s,"World\n"); return s; };
terminal(stdout)
(hello)
(world) ;
return 0;
}
Wat mis ik hier?
Antwoord 1, autoriteit 100%
Het is geen recursieve functieaanroep, bekijk het stap voor stap:
terminal(stdout)
– dit retourneert eenvoudig een lambda diestdout
- Het resultaat van 1. wordt aangeroepen met de lambda
hello
, die de lambda uitvoert (func(term)
), waarvan het resultaat wordt doorgegeven aanterminal()
, die eenvoudig een lambda retourneert zoals in 1. - Het resultaat van 2. wordt aangeroepen met de lambda
world
, die hetzelfde doet als 2, deze keer wordt de geretourneerde waarde weggegooid…
heeft vastgelegd
Antwoord 2, autoriteit 58%
De aanroep zelf is niet recursief. Het retourneert een functie-object dat, indien aangeroepen, terminal
opnieuw zal aanroepen om nog een ander functie-object te genereren.
Dus terminal(stdout)
retourneert een functor die stdout
vastlegt en kan worden aangeroepen met een ander functie-object. Door het opnieuw aan te roepen, (hello)
, wordt de hello
functor aangeroepen met de vastgelegde term stdout
, met als output "Hello"
; de roept terminal
aan en retourneert een andere functor die deze keer de retourwaarde van hello
vastlegt – wat nog steeds stdout
is. Die functor aanroepen, (world)
, precies hetzelfde, met "World"
als output.
Antwoord 3, autoriteit 29%
De sleutel hier is om te begrijpen dat dit geldig is:
world(hello(stdout));
en zal “Hallo wereld” afdrukken. De recursieve reeks lambda’s kan worden uitgerold als
#include <cstdio>
auto terminal = [](auto term) // <---------+
{ // |
return [=] (auto func) // | ???
{ // |
return terminal(func(term)); // >---------+
};
};
/*
terminal(stdout) -returns> anonymous_lambda which captures stdout (functor)
anonymous_lambda(hello) is called, func(term) is hello(stdout) and prints "Hello" and returns stdout, the anonymous_lambda -returns> terminal(stdout)
(the above 2 lines start again)
terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor)
anonymous_lambda(world) is called, func(term) is world(stdout) and prints "World" and returns stdout, the anonymous_lambda -returns> terminal(stdout)
terminal(stdout) is called and -returns> anonymous_lambda which captures stdout (functor)
nobody uses that anonymous_lambda.. end.
*/
auto main() -> int
{
auto hello =[](auto s){ fprintf(s,"Hello\n"); return s; };
auto world =[](auto s){ fprintf(s,"World\n"); return s; };
world(hello(stdout));
terminal(stdout)
(hello)
(world) ;
return 0;
}
Antwoord 4, autoriteit 22%
Het kan intern worden vertaald in iets dat er als volgt uitziet:
#include <cstdio>
template <typename T>
struct unnamed_lambda
{
unnamed_lambda(T term) : captured_term(term) {}
template <typename A>
unnamed_lambda operator()(A func);
T captured_term;
};
struct terminal_lambda
{
template <typename A>
unnamed_lambda<A> operator()(A term)
{
return unnamed_lambda<A>{term};
}
};
terminal_lambda terminal;
template <typename T>
template <typename A>
unnamed_lambda<T> unnamed_lambda<T>::operator()(A func)
{
return terminal(func(captured_term));
}
struct Hello
{
FILE* operator()(FILE* s)
{
fprintf(s, "Hello\n");
return s;
}
};
struct World
{
FILE* operator()(FILE* s)
{
fprintf(s, "World\n");
return s;
}
};
int main()
{
Hello hello;
World world;
unnamed_lambda<FILE*> l1 = terminal(stdout);
unnamed_lambda<FILE*> l2 = l1(hello);
unnamed_lambda<FILE*> l3 = l2(world);
// same as:
terminal(stdout)(hello)(world);
}
Eigenlijk is dit wat de compiler achter de schermendoet met lambda’s (met enige benadering).
Antwoord 5, autoriteit 18%
Ik denk dat de bron van verwarring voortkomt uit het lezen van een lambda-aangifte als een lambda-oproep. Inderdaad hier:
auto terminal = [](auto term) // <---------+
{ // |
return [=] (auto func) // | ???
{ // |
return terminal(func(term)); // >---------+
};
};
de auteur heeft zojuist een lambda terminal
gedeclareerd die één willekeurig argument term
neemt en een naamloze lambda teruggeeft, meer niet! Laten we eens kijken naar deze naamloze lambda, het:
- accepteert een oproepbaar object
func
als argument en roept het aan op de door kopiëren vastgelegde parameterterm
en - retourneert het resultaat van de aangeroepen terminal met het resultaat van de aanroep
func(term)
; dus het retourneert een andere naamloze lambda die het resultaat vanfunc(term)
vastlegt, maar deze lambda is nu niet aangeroepen, er is geen recursie.
Nu zou de truc in het algemeen duidelijker moeten zijn:
terminal(stdout)
retourneert een naamloze lambda die stdout heeft vastgelegd.(hello)
noemt deze naamloze lambda-passing als arg de hallo callable. Dit wordt aangeroepen op de eerder vastgelegde stdout.hello(stdout)
retourneert opnieuw stdout die wordt gebruikt als argument van een aanroep naar terminal, en retourneert een andere naamloze lambda die stdout heeft vastgelegd.(world)
hetzelfde als 2.
Antwoord 6, autoriteit 7%
-
terminal(stdout) retourneert een functie, laten we deze functie
x
noemen, met paramfunc
. Dus:terminal(stdout) ==> x(func) { return terminal(func(stdout)) };
-
Nu roept terminal(stdout)(hallo) functie
x(hello)
aan:terminal(stdout)(hello) ==> x(hello) { return terminal(hello(stdout)) };
Hierdoor wordt de functie
hello
aangeroepen en wordt functiex
opnieuw geretourneerd. -
Nu roept terminal(std)(hello)(world) functie
x(world)
aan:terminal(stdout)(hello) ==> x(world) { return terminal(world(stdout)) };
Hierdoor wordt de functie
world
aangeroepen en wordt de functiex
opnieuw geretourneerd. Functiex
wordt nu niet meer aangeroepen omdat er geen param meer is.