美文网首页
JavaScript实现Y组合子函数

JavaScript实现Y组合子函数

作者: 泓荥 | 来源:发表于2017-12-09 21:36 被阅读0次

    什么是Y组合子函数?

    通俗来讲就是用来解决匿名递归函数实现的。

    实现过程

    我们知道,具名递归函数可以直接通过函数名称来实现自己调用自己,即递归调用,那么匿名函数怎么实现呢?首先来看一段代码:

    function Y(g) { return g(g) }
    

    Y函数的参数名称为g,g为函数类型,然后对g进行递归调用。看到这里是否发现了些相似的点,由于匿名函数没有具体名称,因此我们声明一个函数Y对g进行包装,以便函数g可以进行递归调用,接下来我们来看看具体的调用方法:

    Y(Y)
    

    很容易意料到,程序会报错。现在看来函数仍是具有名称的,让我们写的更抽象点儿:

    (function (g) {
      return g(g);                          
    })(function (g) {
      return g(g);                             
    })
    

    其实可以看到,我们仅仅是对Y(Y)这种写法进行了改写,他们本质都是相同的。立即执行函数中的参数就是我们之前编写的函数Y,而该立即执行函数中的函数体与我们的Y函数并没有任何联系,在这里,他们仅仅只是代码一样而已,后面我们会看到差别。现在,程序并不能正确的跑起来,让我们以阶乘为例,看看会有怎样的事情发生:

    (function (g) {
        return g(g);
    })(function (g) {
        return function (x) {
            if (x === 0) {
                return 1;
            }
            return x;
        }
    })
    

    在这里,我们简单地改写了一下Y函数的函数体,他返回了另外的一个函数,该函数的功能很简单,如果传入的参数为0,则返回1,否则返回原值,我们可以先来看看这样的执行结果是什么:

    (function (g) {
        return g(g);
    })(function (g) {
        return function (x) {
            if (x === 0) {
                return 1;
            }
            return x;
        }
    })(10)
    

    毫无疑问地,结果为10,当10改为0时,返回的结果为1。在这里,立即执行函数的函数体中的g(g)其实仅仅只调用了一次Y函数,因为我们还没有对Y进行递归处理,接下来看看完整功能的函数是怎样的:

    (function (g) {
        return g(g);
    })(function (g) {
        return function (x) {
            if (x === 0) {
                return 1;
            }
            return x * g(g)(x - 1);
        }
    })
    

    是的,我们将return x改为return x * g(g)(x - 1),就实现了我们所需要的阶乘功能。想想这是为什么?此处的g(g)(x - 1)是什么意思?其实就像我之前说的那样,g(g)代表我们的Y函数会对自身进行一次递归调用,由于现在Y函数的返回值为一个函数,它需要一个数值作为参数,因为我们将(x - 1)传入了进去,这与我们写具名函数的递归写法时是一样的。

    我们能否让这个函数更通用些呢?这里,阶乘的逻辑是与Y函数写在一起的,我们试着将它抽象出来:

    function Y(f) {
        return (function (g) {
            return g(g);
        })(function (g) {
            return function (x) {
                return f(x, g(g));
            }
        });
    }
    
    function fc(x, g) {
        if (x === 0) {
            return 1;
        }
        return x * g(x - 1);
    }
    
    console.log(Y(fc)(4));
    

    顺便一说,忘记之前的Y函数吧,现在的Y函数与之前不一样。可以看到,我们仅仅只是将之前的阶乘逻辑提取出来,定义为fc函数,事实上这依旧不够通用,我们先对fc函数进行修改:

    function fc(g) {
        return function (x) {
            return x === 0 ? 1 : x * g(x - 1);
        }
    }
    

    所做的是将fc的两个参数变为逐个传递(即柯里化函数),然后立即函数的变化:

    function Y(f) {
        return (function (g) {
            return g(g);
        })(function (g) {
            return f(function (x) {
                return g(g)(x);
            });
        });
    }
    
    function fc(g) {
        return function (x) {
            return x === 0 ? 1 : x * g(x - 1);
        }
    }
    
    console.log(Y(fc)(4));
    

    这里,我们可以看到立即执行函数的参数部分,仅仅只是对自身进行递归调用,逻辑交由外部函数处理。

    END.

    相关文章

      网友评论

          本文标题:JavaScript实现Y组合子函数

          本文链接:https://www.haomeiwen.com/subject/wpppixtx.html