funciones examples c++ lambda c++14

examples - std c++



Lambda sobre Lambda en C++ 14 (6)

¿Cómo termina / finaliza la siguiente llamada recursiva lambda?

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

¿Qué me estoy perdiendo por aquí?

Running code


  1. terminal (stdout) devuelve una función, llamémosla función x , con param func . Entonces:

    terminal(stdout) ==> x(func) { return terminal(func(stdout)) };

  2. Ahora la terminal (stdout) (hola) llama a la función x(hello) :

    terminal(stdout)(hello) ==> x(hello) { return terminal(hello(stdout)) };

    Esto hace que se llame a la función hello y devuelve la función x nuevamente.

  3. Ahora la terminal (std) (hola) (world) llama a la función x(world) :

    terminal(stdout)(hello) ==> x(world) { return terminal(world(stdout)) };

    Esto hace que se llame a la función world y devuelve la función x nuevamente. La función x ahora ya no se llama ya que no hay más param.


Creo que la fuente de confusión proviene de leer una declaración lambda como una llamada lambda. De hecho aquí:

auto terminal = [](auto term) // <---------+ { // | return [=] (auto func) // | ??? { // | return terminal(func(term)); // >---------+ }; };

el autor acaba de declarar un terminal lambda que toma un term argumento arbitrario y devuelve un lambda sin nombre, ¡nada más! Veamos esta lambda sin nombre:

  • acepta un func objeto invocable como argumento y lo llama en el term parámetro capturado por copia y
  • devuelve el resultado de la terminal llamada con el resultado de la llamada func(term) ; por lo tanto, devuelve otra lambda sin nombre que captura el resultado de func(term) , pero esta lambda no se llama ahora, no hay recursividad.

Ahora el truco en lo principal debería ser más claro:

  1. terminal(stdout) devuelve un lambda sin nombre que ha capturado stdout.
  2. (hello) llama a este lambda sin nombre que pasa como arg el hola invocable. Esto se llama en el stdout previamente capturado. hello(stdout) devuelve nuevamente stdout, que se utiliza como argumento de una llamada a la terminal, devolviendo otra lambda sin nombre que ha capturado stdout.
  3. (world) igual que 2.

La clave aquí es entender que esto es válido:

world(hello(stdout));

e imprimirá "Hola Mundo". La serie recursiva de lambdas se puede desenrollar como

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

Ejemplo de Coliru


La llamada en sí no es recursiva. Devuelve un objeto de función que, si se llama, volverá a llamar al terminal para generar otro objeto de función.

Entonces terminal(stdout) devuelve un functor que captura stdout y se puede llamar con otro objeto de función. Al volver a llamarlo (hello) , se llama al functor hello con el término stdout capturado y se muestra "Hello" ; el terminal llamadas y devuelve otro functor que esta vez captura el valor de retorno de hello , que todavía es stdout . Llamando a ese functor, (world) , de nuevo igual, dando como resultado "World" .


No es una llamada de función recursiva, mírela paso a paso:

  1. terminal(stdout) : esto simplemente devuelve un lambda que ha capturado stdout
  2. El resultado de 1. se llama con el lambda hello , que ejecuta lambda ( func(term) ), cuyo resultado se pasa a terminal() , que simplemente devuelve un lambda como en 1.
  3. El resultado de 2. se llama con el world lambda, que hace lo mismo que 2, esta vez el valor de retorno se descarta ...

Se puede traducir internamente en algo que se ve de la siguiente manera:

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

DEMO EN VIVO

En realidad, esto es lo que hace el compilador detrás de escena con lambdas (con alguna aproximación).