Lambda演算之Y-Combinator的推导(JS描述)

简介:

上一节中,我们讲到了如何使用λ演算来描述自然数,可以看出λ演算的表现力确实非常强大,然而遗憾的是,由于lambda演算中使用的都是匿名函数,所以它无法很直观地表述递归。 如果缺少了递归,λ演算的能力无疑会大打折扣。

所有基于λ演算的语言中,其实都是支持对过程进行命名的,那为什么这里我们还需要探讨匿名函数的递归呢? 1. 为了保持纯洁性,我们希望仅仅通过λ演算的规则本身来解决递归的问题。 2. 部分语言对过程的命名必须等到该过程定义完成后才可以进行,这个时候我们必须找到一种方式使其能够很容易地获得递归的能力。

下面我们就是用factorial函数为原型,来看看如何使用lambda演算基本的规则,为其增加递归的能力。

传统定义方式,不过这个方式不符合我们的需求,因为它使用到了函数自身fact。

var fact = function(n){
    return n <= 1 ? 1 : n * fact(n - 1);
};

几乎所有的问题解决过程都可以通过分层来解决,既然它不允许我们直接使用fact,那我们是不是可以直接把fact作为函数传入呢?

var fac = function(f, n){
    return n <= 1 ? 1 : n * f(f, n - 1);
};

为了后续处理方便,我们先对该函数进行currying。

var fac1 = function(f){
    return function(n) {
        return n <= 1 ? 1 : n * f(f)(n - 1);
    };
};

((self self) (dec n)) 是不是有点丑陋,我们知道λ演算是数学家们搞出来的,他们对构造形式是有很严重的洁癖的。既然我们就是函数本身,为什么还需要f(f)这样多此一举呢。 如果我们的定义能变成下面这样就比较完美了。

var fac = function(f){
    return function(n) {
        return n <= 1 ? 1 : n * f(n - 1);
    };
};

这样也许更符合数学家们的口味,但是该函数是无法被直接执行的,这个使我们暂时假定的factorial的理想函数形式。为了构造这个理想函数形式,我们不妨把它变成f(n - 1),下面我们试着把f(f)拎出来,用作参数f传入。

var fac2 = function(f) {
    return function(n) {
        var h = function(g){
            return n <= 1 ? 1 : n * g(n - 1);
        };
        return h(f(f));
    };
};

可以看出h已经是fac函数的理想形式,不过它还包含自由变量n,没关系,我们可以把它先约束起来。

var fac3 = function(f) {
    return function(n) {
        var h = function(g){
            return function(m){
                return m <= 1 ? 1 : m * g(m - 1);
            };
        };
        return h(f(f))(n);
    };
};

让我们把h抽出来,作为一个单独的函数,它刚好就是我们要构造的factorial函数的理想形式,并给出fac4函数的定义。

var fac_gen = function(g){
    return function(m){
        return m <= 1 ? 1 : m * g(m - 1);
    };
};
var fac4 = function(f){
    return function(n) {
        return fac_gen(f(f))(n);
    };
};

其实这个时候,fact4和factorial的计算过程已经没有什么关系了,换而言之,其实它可以更通用一些,我们不妨给它取个通用些的名字。

var y = function(gen){
    return function(f){
        return function(n){
            return gen(f(f))(n);
        };
    };
};
var fac5 = y(fac_gen);

事实确是如此,该函数有一种神奇的能力,它不仅使可以计算factorial,它事实上可以计算所有形如factorial理想形式的函数递归。

var fib_gen = function(f){
    return function(n){
        return n <= 2 ? 1 : f(n - 1) + f(n - 2);
    };
};
y(fac_gen)(y(fac_gen))(5) //120
y(fib_gen)(y(fib_gen))(6) //8

我们看下我们的调用,都是形如:y(_gen)(y(_gen))(n)的形式,还是相当丑陋的,我们不如精简下,简化其调用过程。

var y1 = function(gen) {
    var g = function(f) {
        return function(n){
            return gen(f(f))(n);
        };
    };
    return g(g);
};

更常规的写法

var Y = function(gen){
    return (function(g){
        return g(g);
    })(function(f){
        return function(n){
            return gen(f(f))(n);
        };
    });
};

Y(fac_gen)(5) //120

说了这么多,这个东西和Y Combinator又有什么关系?其实,我们最终得出的Y函数,其实就是我们所要苦苦找寻的Y Combinator函数,它具有性质Y(F) = F(Y(F)) = F(F(Y(F)))。

目录
相关文章
|
8月前
|
存储 缓存 JavaScript
请描述一种JavaScript内存泄漏的情况,并说明如何避免这种情况的发生。
JavaScript内存泄漏常由闭包引起,导致无用对象滞留内存,影响性能。例如,当一个函数返回访问大型对象的闭包,即使函数执行完,对象仍被闭包引用,无法被垃圾回收。防止泄漏需及时解除引用,注意事件监听器清理,使用WeakMap或WeakSet,定期清理缓存,以及利用性能分析工具检测。
44 2
|
8月前
|
JavaScript 前端开发 算法
描述 JavaScript 中的垃圾回收机制。
描述 JavaScript 中的垃圾回收机制。
66 1
|
8月前
|
存储 移动开发 JavaScript
NUS CS1101S:SICP JavaScript 描述:五、使用寄存器机进行计算(1)
NUS CS1101S:SICP JavaScript 描述:五、使用寄存器机进行计算(1)
93 0
|
8月前
|
移动开发 JavaScript 前端开发
游戏框架 - 描述Phaser、Three.js等JavaScript游戏框架的核心功能和使用场景。
Phaser是开源2D游戏引擎,适合HTML5游戏,内置物理引擎和强大的图形渲染功能,适用于2D游戏,如消消乐。Three.js是基于WebGL的3D库,用于创建和显示3D图形,支持交互和多种3D效果,广泛应用在游戏、可视化等多个领域。两者各有侧重,选择取决于项目需求和图形交互要求。
200 3
|
3月前
|
Web App开发 JavaScript 前端开发
javascript主要特点有哪些,简单描述javascript的特点
javascript主要特点有哪些,简单描述javascript的特点
68 0
|
7月前
|
存储 前端开发 JavaScript
JavaScript 事件循环的详细描述
【6月更文挑战第15天】JavaScript事件循环是单线程非阻塞I/O的关键,通过调用栈跟踪同步函数,任务队列存储异步任务,事件循环在调用栈空时从队列取任务执行。当遇到异步操作,回调函数进入队列,同步代码执行完后开始事件循环,检查并执行任务。微任务如Promise回调在每个宏任务结束时执行,确保不阻塞主线程,优化用户体验。
55 6
|
8月前
|
开发框架 JavaScript 前端开发
描述JavaScript事件循环机制,并举例说明在游戏循环更新中的应用。
JavaScript的事件循环机制是单线程处理异步操作的关键,由调用栈、事件队列和Web APIs构成。调用栈执行函数,遇到异步操作时交给Web APIs,完成后回调函数进入事件队列。当调用栈空时,事件循环取队列中的任务执行。在游戏开发中,事件循环驱动游戏循环更新,包括输入处理、逻辑更新和渲染。示例代码展示了如何模拟游戏循环,实际开发中常用框架提供更高级别的抽象。
45 1
|
8月前
|
前端开发 JavaScript UED
描述 JavaScript 中的事件循环机制。
描述 JavaScript 中的事件循环机制。
29 1
|
8月前
|
存储 监控 JavaScript
NUS CS1101S:SICP JavaScript 描述:五、使用寄存器机进行计算(2)
NUS CS1101S:SICP JavaScript 描述:五、使用寄存器机进行计算(2)
73 1
|
8月前
|
存储 JavaScript 前端开发
NUS CS1101S:SICP JavaScript 描述:四、元语言抽象(5)
NUS CS1101S:SICP JavaScript 描述:四、元语言抽象(5)
40 1