C ++ 14中的Lambda-Over-Lambda


69

接下来的递归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;

}

我在这里想念什么?

Running code


24
此列表中的一个很好:gnu.org/fun/jokes/helloworld.html
stefaanv 2014年

其实,我感兴趣的(是/可)称为如何,所以我发布了跟进的问题:stackoverflow.com/questions/25619769/...
stefaanv

1

也许您打算return terminal(func)(term);代替return terminal(func(term));?请注意,在执行任何操作之前,terminal需要使用参数调用两次
亚伦·麦克戴德2014年

27
哦,天哪,这个:“ auto main()-> int”太糟糕了。当旧工具已经非常适合工作时,尝试使用新工具并不有趣。还是“ int main()”这样的2010?
xryl669

Answers:


45

这不是递归函数调用,请分步看一下:

  1. terminal(stdout) -这只是返回已捕获的lambda stdout
  2. 用lambda调用1.的结果,该lambdahello执行lambda(func(term)),将其结果传递给terminal(),该函数简单地返回一个lambda,如1所示。
  3. 用lambda调用2的结果,该结果与2world相同,但是这次返回值被丢弃了。

26

调用本身不是递归的。它返回一个功能对象,如果被调用,它将terminal再次调用以生成另一个功能对象。

因此terminal(stdout)返回一个函子,该函子捕获stdout并可以用另一个函数对象调用。再次(hello)调用,hello用捕获的项调用函子stdout,输出"Hello";调用terminal并返回另一个函子,这次它捕获-的返回值,hello该值仍然是stdout(world)再次调用该函子,再次输出"World"


谢谢,我正在浏览一篇文章,作者在其中使用了两个不同的lambda,但是我在不知不觉中尝试将其包装在原始文本上,发现它有点令人困惑。感谢您的澄清。
2014年

13

这里的关键是要了解这是有效的:

world(hello(stdout));

并打印“ Hello World”。递归系列的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示例


10

可以在内部将其转换为如下所示的内容:

#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

实际上,这就是编译器使用lambda(有一些近似值)在后台执行的操作


1
正确(在技术上令人印象深刻),但与OP提出的问题无关。
Quuxplusone

8

我认为混淆的根源来自将lambda声明读取为lambda调用。确实在这里:

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

作者刚刚声明了一个lambda terminal,它接受一个任意参数,term并返回一个未命名的lambda,仅此而已!让我们看一下这个未命名的lambda:

  • 接受可调用对象func作为参数,并在复制捕获的参数上调用它term然后
  • 返回带有调用结果的终端的结果func(term); 因此它返回另一个未命名的lambda来捕获的结果func(term),但现在尚未调用该lambda,因此没有递归。

现在,主要的窍门应该更加清楚:

  1. terminal(stdout) 返回已捕获标准输出的未命名lambda。
  2. (hello)将此未命名的lambda称为arg传递给hello可调用。在先前捕获的标准输出上调用此方法。hello(stdout)再次返回stdout,用作对终端的调用的参数,并返回另一个已捕获stdout的未命名lambda。
  3. (world) 与2相同

3
  1. terminal(stdout)返回一个函数,我们将其称为xparam func。所以:

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

  2. 现在terminal(stdout)(hello)调用function x(hello)

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

    这导致hello函数被调用并x再次返回函数。

  3. 现在terminal(std)(hello)(world)调用function x(world)

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

    这导致world函数被调用并x再次返回函数。x现在不再调用函数,因为没有更多参数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.