简体   繁体   English

C ++ 14中的Lambda-Over-Lambda

[英]Lambda-Over-Lambda in C++14

How following recursive lambda call ends/terminates ? 接下来的递归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;

}

What am I missing over here ? 我在这里想念什么?

Running code

It's not a recursive function call, look at it step-by-step: 这不是递归函数调用,请分步看一下:

  1. terminal(stdout) - this simply returns a lambda which has captured stdout terminal(stdout) -这只是返回捕获了stdout的lambda
  2. The result of 1. is called with the lambda hello , which executes the lambda ( func(term) ), the result of which is passed to terminal() , which simply returns a lambda as in 1. 用lambda hello调用1.的结果,它执行lambda( func(term) ),其结果被传递给terminal() ,后者仅像1一样返回lambda。
  3. The result of 2. is called with the lambda world , which does the same as 2, this time the return value is discarded... 2.的结果被lambda world调用,它的功能与2相同,但是这次返回值被丢弃了。

The call itself is not recursive. 调用本身不是递归的。 It returns a function object which, if called, will call terminal again to generate yet another function object. 它返回一个功能对象,如果调用该功能对象,它将再次调用terminal以生成另一个功能对象。

So terminal(stdout) returns a functor which captures stdout and can be called with another function object. 因此, terminal(stdout)返回一个函子,该函子捕获stdout并可以与另一个函数对象一起调用。 Calling it again, (hello) , calls the hello functor with the captured term stdout , outputting "Hello" ; 再次调用(hello) ,使用捕获的术语stdout调用hello函子,输出"Hello" ; the calls terminal and returns another functor which this time captures the return value of hello - which is still stdout . 调用terminal并返回另一个函数,这次捕获了hello的返回值-仍然是stdout Calling that functor, (world) , just the same again, outputting "World" . 再次调用该函子(world) ,输出"World"

The key here is to understand that this is valid: 这里的关键是要了解这是有效的:

world(hello(stdout));

and will print "Hello World". 并打印“ Hello World”。 The recursive series of lambdas can be unrolled as 递归的lambda系列可以展开为

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

}

Coliru example Coliru示例

It can be internally translated into something that looks as follows: 可以在内部将其转换为如下所示的内容:

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

LIVE DEMO 现场演示

Actually this is what the compiler does behind the scene with lambdas (with some approximation). 实际上,这就是编译器使用lambda(有一些近似值) 在后台执行的操作

I think that the source of confusion comes from reading a lambda declaration as a lambda call. 我认为混淆的根源来自将lambda声明读取为lambda调用。 Indeed here: 确实在这里:

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

the author just declared a lambda terminal which takes one arbitrary argument term and returns an unnamed lambda, nothing more! 作者刚刚声明了一个lambda terminal ,该terminal接受一个任意参数term并返回未命名的lambda,仅此而已! Let's look at this unnamed lambda, it: 让我们看一下这个未命名的lambda:

  • accepts a callable object func as argument and calls it on the copy-captured parameter term and 接受可调用对象func作为参数,并在复制捕获的参数term上调用它,并且
  • returns the result of terminal called with the result of the call func(term) ; 返回带有调用func(term)的结果的terminal的结果; so it returns another unnamed lambda that captures the result of func(term) , it but this lambda is not called by now, there is no recursion. 因此它返回另一个未捕获的lambda来捕获func(term)的结果,但是现在尚未调用此lambda,因此没有递归。

Now the trick in the main should be more clear: 现在主要的技巧应该更加清楚:

  1. terminal(stdout) returns an unnamed lambda which has captured stdout. terminal(stdout)返回捕获了stdout的未命名lambda。
  2. (hello) calls this unnamed lambda passing as arg the hello callable. (hello)将此未命名的lambda称为arg传递给可调用的hello。 This gets called on the stdout previously captured. 这将在先前捕获的标准输出上调用。 hello(stdout) returns again stdout which is used as argument of a call to terminal, returning another unnamed lambda which has captured stdout. hello(stdout)再次返回stdout,用作对终端的调用的参数,并返回另一个捕获了stdout的未命名lambda。
  3. (world) same as 2. (world)与2相同。
  1. terminal(stdout) returns a function, let's call it function x , with param func . terminal(stdout)返回一个带有参数func函数x So: 所以:

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

  2. Now terminal(stdout)(hello) calls function x(hello) : 现在terminal(stdout)(hello)调用函数x(hello)

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

    This results in hello function get called and returns function x again. 这导致hello函数被调用并再次返回函数x

  3. Now terminal(std)(hello)(world) calls function x(world) : 现在terminal(std)(hello)(world)调用函数x(world)

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

    This results in world function get called and returns function x again. 这导致world函数被调用并再次返回函数x Function x now is not called any more as there is no more param. 由于不再有参数,现在不再调用函数x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM