【问题标题】:Call lambda without binding it to an identifier调用 lambda 而不将其绑定到标识符
【发布时间】:2013-03-11 17:40:08
【问题描述】:

浏览一些互联网板我遇到了这个小挑战:

“用你喜欢的语言实现一个递归匿名函数”

显然,这很容易使用 std::function/function 指针。

我真正感兴趣的是,如果不将 lambda 绑定到标识符,这是否可行?

类似的东西(忽略明显的无限递归):

[](){ this(); }();

【问题讨论】:

  • 使用fixed point combinator,您可以将任何函数转换为递归函数。
  • 您是否想要没有标识符,或者您真的是要匿名? C++ 可以有非匿名名称,这些名称标识符组成,但它们本身不是标识符。
  • 没有标识符我的意思是没有变量名

标签: c++ c++11 lambda


【解决方案1】:

当然,在 C++ 中,要调用 any 函数,您必须将其绑定到某处的标识符,这只是由于语法限制。但是,如果您接受参数足够未命名,则可以在 C++ 中创建一个 y-combinator 版本,该版本可以很好地递归而无需“命名”。

现在,这真的很难看,因为我不知道如何处理typedef for a recursive lambda。所以它只是使用了很多演员滥用。但是,它可以工作,并打印FLY!!,直到由于堆栈溢出而出现段错误。

#include <iostream>

typedef void(*f0)();
typedef void(*f)(f0);

int main() {
    [](f x) {
        x((f0)x);
    } ([](f0 x) {
        std::cout<<"FLY!!\n";
        ((f)x)(x);
    });
}

这两个 lambda 表达式是未命名的,因为它们都没有在任何地方显式分配给 name。第二个 lambda 是真正的主力,它基本上通过使用第一个 lambda 以参数的形式获取对自身的引用来调用自身。

以下是你将如何使用它来做一些“有用”的工作:

#include <iostream>

typedef int param_t;
typedef int ret_t;

typedef void(*f0)();
typedef ret_t(*f)(f0, param_t);

int main() {
    /* Compute factorial recursively */
    std::cout << [](f x, param_t y) {
        return x((f0)x, y);
    } ([](f0 x, param_t y) {
        if(y == 0)
            return 1;
        return y*((f)x)(x, y-1);
    }, 10) << std::endl;
}

【讨论】:

    【解决方案2】:

    你可以作弊吗?

    void f(){
      []{ f(); }();
    }
    

    它是递归的——至少是间接的。

    否则,不,没有办法在不给它命名的情况下引用 lambda 本身。

    【讨论】:

    • 有趣的解决方法,但函数名也是标识符,对吧?
    • @user:当然,但问题并没有禁止间接递归。 :)
    【解决方案3】:

    没有函数/方法的标识符,是否足够接近!?

    struct A
    {
        void operator()()
        {
            [&]()
            {
                (*this)();
            }();
        }
    };
    

    打电话

    A{}(); // Thanks MooningDuck
    

    【讨论】:

    • 这与我的回答基本相同,通过命名函数进行间接寻址。另外,WTF new A.
    • @MM。您可以通过将运算符设为静态或(在 C++11 中)A{}() 来避免 WTF。此外,函数的“名称”是A::operator(),因此它实际上与 Xeo 的相同。
    • 标识符是operator()。您甚至可以致电a.operator()();。另外,A()().
    • @Xeo:我们可以按照你说的方式调用它,但是标识符不应该有括号。所以,这只是一种称呼方式。但operator() 不是标识符。
    • @MM。我刚刚测试过,运营商似乎不能是静态的。你是对的,名字不是标识符。这有一个名称,但该函数没有标识符。它只有一个 id。
    【解决方案4】:

    我似乎想出了自己的解决方案:

    #include <iostream>
    
    int main()
    {
        std::cout<<"Main\n";
        [&](){
            std::cout<<"Hello!\n";
            (&main+13)();
             }();
    }
    

    第一次调用 cout 只是为了表明它没有调用 main。

    我通过反复试验得出了 13 偏移量,如果有人能解释为什么它是这个值,那就太好了。

    【讨论】:

    • 这是完全未定义的行为。
    • 13 在你的编译器+编译器标志+处理器+操作系统组合上,其他人的段错误!耶!
    猜你喜欢
    • 2010-10-04
    • 1970-01-01
    • 2023-02-22
    • 1970-01-01
    • 1970-01-01
    • 2012-03-17
    • 2010-10-18
    • 1970-01-01
    • 2015-02-06
    相关资源
    最近更新 更多