通过通用lambda理解Y Combinator

时间:2016-02-24 17:26:56

标签: c++ recursion functional-programming c++14 y-combinator

在构建一个基于lambda的小型元编程库时,我有必要在C ++ 14泛型lambda中使用递归来实现left-fold

我自己的解决方案是将lambda本身作为其参数之一传递,如下所示:

template <typename TAcc, typename TF, typename... Ts>
constexpr auto fold_l_impl(TAcc acc, TF f, Ts... xs)
{
    // Folding step.
    auto step([=](auto self)
    {
        return [=](auto y_acc, auto y_x, auto... y_xs)
        {
            // Compute next folding step.
            auto next(f(y_acc, y_x));

            // Recurse if required.
            return static_if(not_empty(y_xs...))
                .then([=]
                    {
                        // Recursive case.
                        return self(self)(next, y_xs...);
                    })
                .else_([=]
                    {
                        // Base case.
                        return next;
                    })();
        };
    });

    // Start the left-fold.
    return step(step)(acc, xs...);
}

step是从递归开始的“主要”lambda。它返回一个带有所需左折签名的函数(累加器,当前项,剩余项......)

该函数使用self(self)(next, y_xs...)递归调用自身。

我最近遇到this proposal想要在标准库中添加 Y Combinator ,在阅读之后,它看起来与我在这里做的非常相似。 / p>

不幸的是,Y Combinator的概念仍然没有为我“点击” - 我遗漏了一些东西,我无法想象如何概括我对self参数所做的任何功能,避免{ {1}}样板。

关于此事,我已阅读this excellent StackOverflow answer,但仍然没有为我“点击”。

(从那个答案)以这种方式定义递归因子:

step

fact = (recurs) => (x) => x == 0 ? 1 : x * recurs(x - 1); 参数似乎与我的recurs参数具有相同的作用。我不明白的是,self如何在不将recurs再次传递给自己的情况下进行调用。

我必须像这样致电recursself

然而,

self(self)(params...)被称为recurs

尝试调用recurs(params...)会导致编译器错误,通知我self(params...)只需要一个参数(这是self lambda参数)。< / p>

我在这里缺少什么?我怎么能以这样的方式重写我的auto self lambda,使得它的递归可以通过使用Y Combinator来推广?

1 个答案:

答案 0 :(得分:5)

这是一个y组合,其中lambda传递一个不需要传递的递归:

template<class F>
struct y_combinate_t {
  F f;
  template<class...Args>
  decltype(auto) operator()(Args&&...args)const {
    return f(*this, std::forward<Args>(args)...);
  }
};
template<class F>
y_combinate_t<std::decay_t<F>> y_combinate( F&& f ) {
  return {std::forward<F>(f)};
};

然后你做:

  return y_combinate(step)(acc, xs...);

并更改

                   return self(self)(next, y_xs...);

                   return self(next, y_xs...);

这里的技巧是我使用了一个非lambda函数对象,它可以访问自己的this,我将其作为第一个参数传递给f