我认为这不能称为“定点递归”,因为它过于简单。但是,我最近意识到它实际上可能是。
我是否有效地实现了定点递归?
这是有问题的功能:
/* recursive kleisli fold */
var until = function(f) {
return function(a) {
return kleisli(f, until(f))(a);
};
};
这是一些额外的背景:
// The error monad's bind
var bind_ = function(f, m) { return m.m === Success ? f(m.a) : m; };
var bind = function(f, m) {
return m !== undefined && m.m !== undefined && m.a !== undefined ? bind_(f, m) : m;
};
var kleisli = function(f1, f2) {
return function(a) {
return bind(f2, f1(a));
};
};
代码的其余部分为here,但上面的代码段应足以跟随。
答案 0 :(得分:6)
定点组合子的定义是函数F
,它接受函数f
并返回函数p
,以便
给定F(f) = p
然后p = f(p)
可以编写许多可能的定点组合器。不要让直截了当让你认为某些东西不是定点组合者;这是JavaScript中的标准定义,非常简单:
var fix = function(f) {
return function(x) {
return f(fix(f))(x)
}
};
然后用一个用法来计算阶乘的定点,用:
var fact = function(f) {
return function(n) { return (n == 0) ? 1 : (n * f(n - 1)) }
};
alert(fix(fact)(7)); // alerts us with 5040.
有关不同定点组合子(Y组合子)的示例,请参阅this helpful blog post。
让我们看看你的until
组合子是否计算了固定点。由于您正在使用monadic函数,定点定义会稍微改变以处理monadic结构,其中F
是一个(monadic)定点组合器
给定F(f) = p
然后p = f* . p
其中f* . p
表示函数p
的Kleisli组合,其函数为f
(在您的代码中,您将编写此kleisli(p, f)
,您可以想到{{1}作为*
)。我将使用这种表示法,因为它比编写JavaScript更短。
让我们展开bind
的定义然后看看我们得到了什么:
until
是until(f) = (until(f))* . f
= (until(f)* . f)* . f
= ((... . f)* . f)* . f
= ... . f* . f* . f (associativity of bind for a monad: (g* . f)* = g* . f*)
= p
吗?
p = f* . p
是的 - 我相信。虽然我不认为这是一个有用的定点。 (我担心我还没有一个好的论据 - 但我认为这基本上是一个最大的固定点,只会出现分歧)。
对我而言,... . f* . f* . f =?= f* . ... . f* . f* . f
中kleisli
的参数应该已被交换。也就是说,我们希望在until
示例中使用Kleisli等效的应用程序,因此我们需要将递归调用fix
的monadic结果传递给until(f)
:
f
让我们展开 var until = function(f) {
return function(a) {
return kleisli(until(f), f)(a);
};
};
的新定义:
until
是until(f) = f* . until(f)
= f* . (f* . until(f))
= f* . f* . ...
= p
吗?是的确如此:
p = f* . p
因为在f *组合的无限链上再添加一个f *组成是相同的功能。
使用你的f* . f* ... = f* . (f* . f* . ...)
函数我遇到了一些分歧问题(某些评估过早发生,因此计算一直运行直到我的堆栈空间不足)。相反,以下似乎对我有用:
kleisli
有关monadic代码定点的更多信息,您可以查看the work of Erkök and Launchbury。