【问题标题】:Is it possible to make a real function type in C++ which is callable with itself?是否可以在 C++ 中创建一个可自行调用的真正函数类型?
【发布时间】:2017-04-01 07:39:32
【问题描述】:

我正在尝试编写递归而不通过 Y-combinator 在 C++ 中引用函数名。但是,在以下尝试中我无法确定函数的类型:

#include <iostream>

using std::cin;
using std::cout;

template<class Function> unsigned long factorial1(Function self, unsigned long n) {
    return n ? n * self(self, n - 1) : 1;
}

unsigned long factorial(unsigned long n) {
    return factorial1(factorial1, n);
}

int main() {
    unsigned long n;
    cin >> n;
    cout << factorial(n) << '\n';
    return 0;
}

编译器无法推断出Function 是什么,我也不能。然后我尝试了以下方法:

#include <iostream>

using std::cin;
using std::cout;

struct Factorial {
    template<class Function> unsigned long operator()(Function self, unsigned long n) const {
        return n ? n * self(self, n - 1) : 1;
    }
};

unsigned long factorial(unsigned long n) {
    return Factorial()(Factorial(), n);
}

int main() {
    unsigned long n;
    cin >> n;
    cout << factorial(n) << '\n';
    return 0;
}

这个,与上面的例子相比,不同的是我把工作函数改成了一个可调用的对象,Function很容易推导出为Factorial,导致下面的组合子的完整实现:​​

#include <iostream>

using std::cin;
using std::cout;

struct Factorial {
    template<class Function> unsigned long operator()(Function self, unsigned long n) const {
        return n ? n * self(self, n - 1) : 1;
    }
};

template<class Function> auto y(Function f) {
    return [f](auto n) {
        return f(f, n);
    };
}

int main() {
    unsigned long n;
    cin >> n;
    cout << y(Factorial())(n) << '\n';
    return 0;
}

问题是,是否可以将结构 Factorial 重写为普通函数?

【问题讨论】:

  • 看你的第一个例子:你为什么不想引用函数名?为什么factorial1 是模板?如果不是factorial1self 会是什么?
  • Y 组合器需要一个更强大的类型系统(模板提供的,正如您自己发现的,也显示了here at Rosetta Code它需要一个不存在 i> 类型系统,如(无类型的)lambda 演算。因此,请尝试使用std::uintptr_t 并在必要时进行强制转换...(顺便说一句:对此评论不提供任何保证。)
  • 人们用 y 组合器回答了我不相关的问题:stackoverflow.com/questions/42796710/…

标签: c++ recursion


【解决方案1】:

你做的有点不对:factorial1 的第一个参数应该已经是 factorial1 类型为 unsigned long(*)(unsigned long) 的固定点,而不是 factorial1 本身,所以不需要提供 self 给它作为论据:

unsigned long factorial1(unsigned long(*self)(unsigned long), unsigned long n) {
    return n ? n * self(n - 1) : 1;
}

C++ 不允许将闭包作为函数指针传递,所以我们必须:

  • std::function 或其他一些包装器作为self 传递。 IMO 不感兴趣。

  • 在编译时使用模板魔法生成定点函数。

第二个选项可以轻松完成:

template<class X, X(*Fn)(X(*)(X), X)>
struct Fix {
    static X Function(X x) {
        return Fn(Fix<X, Fn>::Function, x);
    }
};

现在,Fix&lt;unsigned long, factorial1&gt;::Functiona fixed point of factorial1

unsigned long factorial(unsigned long n) {
    return Fix<unsigned long, factorial1>::Function(n);
};

请注意,这个Fix 实现仍然通过名称引用它自己,因此任何没有类型系统黑客的定点组合器实现。

【讨论】:

【解决方案2】:

你不能直接传递模板,但你可以使用通用 lambda,所以最终看起来你使用了模板:

#define PASS_FUNC(name) [dummy=nullptr](auto&&... args){return name(decltype(args)(args)...);}

template<class Function> unsigned long factorial1(Function self, unsigned long n) {
    return n ? n * self(self, n - 1) : 1;
}

unsigned long factorial(unsigned long n) {
    return factorial1(PASS_FUNC(factorial1), n);
}

但我认为这是一种 hack,因为 lambdas 仍然是函数对象。

【讨论】:

    【解决方案3】:

    您所描述的情况在我看来类似于无限类型或递归类型。如果您尝试自己手动推断类型,您可以看到它是无限的,这可能也是您自己发现的。为了证明这一点,我想将您的 factorial1 函数简化为:

    template <class T> void foobar(T self) {
        self(self);
    }
    

    然后尝试用函数指针而不是模板编写这个函数,以手动推断其类型。

    首先,我们希望foobar 将函数指针作为参数。

    void foobar(void (*self)());
                ^^^^^^^^^^^^^^
    

    但这仍然不是我们想要的,这个函数指针应该将一个指向自身的指针作为参数。

    void foobar(void (*self)(void (*)()));
                             ^^^^^^^^^^
    

    但我们还没有完成,因为我们必须再次添加一个指向自身的指针作为参数

    void foobar(void (*self)(void (*)(void (*)())));
                                      ^^^^^^^^^^
    

    你可以看到它是如何持续不断的。

    void foobar(void (*self)(void (*)(void (*)(void (*)()))));
                                               ^^^^^^^^^^
    
    void foobar(void (*self)(void (*)(void (*)(void (*)(void (*)())))));
                                                        ^^^^^^^^^^
    

    您提供的示例,您设法使用结构来做到这一点,只是通过operator() 模仿它的东西。如果将该函数的名称更改为foobar,它看起来像这样:

    struct Factorial {
        template<class Function> unsigned long foobar(Function self, unsigned long n) const {
            return n ? n * self.foobar(self, n - 1) : 1;
        }
    };
    
    unsigned long factorial(unsigned long n) {
        return Factorial().foobar(Factorial(), n);
    }
    

    因此,您基本上在 foobar 中递归调用 foobar,这与您最初的陈述相矛盾,即您想在不知道/引用其名称的情况下调用该函数。

    【讨论】:

    • “所以你基本上在 foobar 中递归地调用 foobar,这与你最初的声明相矛盾,你想在不知道/引用它的名字的情况下调用函数。”这正是Y combinator 的全部意义所在,也是 OP 想要的。这不是很直观,我知道。
    • 我只是说严格来说第二个例子不是 Y 组合子,而只是一个被operator() 重载伪装的一般递归。
    猜你喜欢
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 2020-03-31
    • 2022-01-14
    相关资源
    最近更新 更多