【问题标题】:Is this an implementation of a fixpoint combinator?这是定点组合器的实现吗?
【发布时间】:2013-07-23 03:53:25
【问题描述】:

我认为这不能称为“定点递归”,因为它太简单了。然而,我最近意识到它实际上可能是。

我是否有效地实现了定点递归?

这是有问题的函数:

/* 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,不过上面的sn-p应该够用了。

【问题讨论】:

    标签: javascript functional-programming monads combinators


    【解决方案1】:

    定点组合器的定义是一个函数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 组合器是否计算定点。由于您使用的是单子函数,因此定点定义会稍作更改以处理单子结构,其中F 是一个(单子)定点组合器

    给定F(f) = p 然后p = f* . p

    其中f* . p 表示函数p 与函数f 的Kleisli 组合(在您的代码中,您将编写此kleisli(p, f),您可以将* 视为bind)。我将使用这种表示法,因为它比编写 JavaScript 更短。

    然后让我们展开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
    

    是的——我相信是的。虽然我不认为这是一个有用的固定点。 (恐怕我还没有很好的论据——但我认为这基本上是一个最大固定点,只会发散)。

    在我看来,untilkleisli 的参数似乎应该被交换了。也就是说,我们希望在fix 示例中做应用程序的Kleisli 等效,因此我们需要将递归调用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 函数时,我遇到了一些分歧问题(一些评估发生得太快,因此计算会一直运行,直到我用完堆栈空间)。相反,以下似乎对我有用:

     var until = function(f) {
         return function(a) {
            return bind(f,until(f)(a));
        };
     };
    

    有关单子代码的定点的更多信息,您可以查看the work of Erkök and Launchbury

    【讨论】:

    • p 在实际实现中使用;我将它用作谓词,将退出条件放在组合器中,而不是放在正在修复的函数中。为了清楚起见,我删除了它,因为我的问题是关于我构建它的方式,而不管退出条件如何;尽管附带说明,如果定点组合器具有退出条件,它仍然是定点组合器吗?
    • 另附注,haskell 中的“错误单子”是 Either,而不是 Maybe :P
    • @JimmyHoffa 哦,抱歉,我没有仔细阅读您的代码。希望我写的仍然适用。在这种情况下,我可能会删除最后一部分,因为它有点切线。
    • 我为什么要使用 bind 而不是 kleisli? kleisli 是否只是多余的,因为它已经在我返回 kleisli 的函数中?
    • 现在在我的问题中删除了 P;当我删除退出条件时,我应该删除它确实毫无意义
    猜你喜欢
    • 2019-03-10
    • 1970-01-01
    • 2021-04-30
    • 2021-04-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-08
    相关资源
    最近更新 更多