用JavaScript中的SKI-Combinators表示Y.

时间:2011-09-09 23:32:04

标签: javascript combinators y-combinator combinatory-logic

我正在摆弄JavaScript中的Cominators,当我偶然发现维基百科说:“Y组合器可以在SKI演算中表达为:Y = S(K(SII) ))(S(S(KS)K)(K(SII)))“,所以我不得不尝试:

var I = function (x) {
            return x;
        };

var K = function (x) {
        return function(){
            return x;}
        };

var S = function (x) {
           return function (y) {
               return function (z) {
                   return x(z)(y(z));
               }
           }
       };

var Y = S (K(S(I)(I))) (S(S(K(S))(K)) (K(S(I)(I))));

Y;    //evals to:
//function (z) {return x(z)(y(z));}

//And this (lifted from Crockford's Site):
var factorial = Y(function (fac) {
    return function (n) {
        return n <= 2 ? n : n * fac(n - 1);
    };
});    //fails:
//RangeError: Maximum call stack size exceeded

我做错了什么?我没有正确翻译那个表达吗?我怎么会这样做有什么问题吗?它甚至有意义吗?关于这样的事情的大部分内容只会让我的大脑想要爆炸,所以这个练习对我来说主要是为了看看我是否理解了这个符号(因此能够将它翻译成JavaScript)。

哦,顺便说一下:是什么让我阅读&amp;再次摆弄是prototype.js实现为Prototype.K实际上是I组合器。有人注意到了吗?

1 个答案:

答案 0 :(得分:6)

这里的问题是您使用的是严格评估的编程语言。 Y-combinator与任何其他定点组合器非常相似,只有在需要调用函数或“懒惰评估”时才能正常工作。

我知道一种解决此问题的方法(one of my professors looked into it a while ago),但它会使您的代码完全无法读取。

下面我已经展示了究竟发生了什么,希望你能看到为什么JavaScript无法处理SKI-calculus的直接实现。


这是JavaScript评估SKI表达式后Y的样子:

var Y = function (q) {
    return (function(p){return q(p(p))})(function(p){return q(p(p))});
};

现在让我们看看如果你将它的函数function (fac) { ... }提供给它会发生什么。我们称之为函数f

var factorial = (function(p){return f(p(p))})(function(p){return f(p(p))});

由于第一个匿名函数应用于参数,因此将对其进行求值:

var factorial = f(
    (function(p){return f(p(p))})(function(p){return f(p(p))})
);

在一种延迟评估的语言中,f的参数现在将保持不变,并且f本身将被评估。但是,因为JavaScript是一种严格评估的语言(或“按值调用”),它想知道在实际运行该函数之前需要将哪个参数传递给函数f。那么,让我们评估一下这个论点,不管吗?

var factorial = f(f(
        (function(p){return f(p(p))})(function(p){return f(p(p))})
    )
);

我想现在你开始看到现在出了问题的地方,以及Y-combinator实际上是如何工作的。在任何情况下,您的JavaScript机器都将耗尽堆栈空间,因为它正在尝试构建对f的无限堆栈调用。