zoukankan      html  css  js  c++  java
  • [翻译]轻松7步,导出Y结合子

    本文译自 "Deriving the Y Combinator in 7 Easy Steps",

    原文链接:http://igstan.ro/posts/2010-12-01-deriving-the-y-combinator-in-7-easy-steps.html

                              轻松7步,导出Y结合子

    在没有原生递归支持的语言中,Y结合子(Y Combinator)是一种实现递归的方式(事实上,它更常被作为一种锻炼程序思维的方式)。要实现Y结合子,要求这种语言支持匿名函数。

    此处,我选择JavaScript来推导Y结合子,从递归阶乘函数的定义开始,一步一步进行变换。

    Step 1

    最初的实现,使用JavaScript内建的递归机制。

    var fact = function (n) {
        if (n < 2) return 1;
        return n * fact(n - 1);
    };
     

    Step 2

    获得递归的最简单方法是什么?我们可以定义一个函数,它接受它自身作为参数,并且用这个参数作为参数,调用这个参数。当然,这是一个无限循环,会导致栈溢出。

    (function (f) {
        f(f);
    })(function (f) {
        f(f);
    });
     

    我们的阶乘函数套用上面的模板,再做点改动,阶乘函数接受一个我们还不知道的参数,所以我们要的是返回一个接受该参数的函数。然后这个函数可以被用于计算阶乘。同时,这可以让我们的阶乘函数不会无限循环下去。

    var fact = (function (f) {
        return function (n) {
            // 终止条件
            if (n < 2) return 1;
    
            //因为f返回一个函数,所以这有一个双重调用。 
            return n * f(f)(n - 1);
        };
    })(function (f) {
        return function (n) {
            // 终止条件
            if (n < 2) return 1;
    
            // 因为f返回一个函数,所以这有一个双重调用。
            return n * f(f)(n - 1);
        };
    });
     

    Step 3

    此时,我们的代码有一些糟糕的重复,让我们把放进一个名叫recur的辅助函数里。

    var recur = function (f) {
        return f(f);
    };
    
    var fact = recur(function (f) {
        return function (n) {
            if (n < 2) return 1;
    
            // 因为f返回一个函数,所以这有一个双重调用。
            return n * f(f)(n - 1);
        };
    });
     

    Step 4

    上面这个版本的问题是,它有两重调用(指的是f(f)(n-1))。我们想去除它,让我们的函数跟接近于递归版本。怎么做呢?

    我们可以使用一个辅助函数,它接受一个数值参数,进行两重调用。这个trick通过把辅助函数放在可见f的作用域里,所以g可以调用f。

    var recur = function (f) {
        return f(f);
    };
    
    var fact = recur(function (f) {
        var g = function (n) {
            return f(f)(n);
        };
    
        return function (n) {
            if (n < 2) return 1;
    
            // 没有双重调用了,函数g接受一个数值参数。
            return n * g(n - 1);
        };
    });
     

    Step 5

    以上版本工作良好,但是它的定义太杂乱了。我们可以把它再清理为一个辅助函数,把阶乘定义尽可能分离出来。

    var recur = function (f) {
        return f(f);
    };
    
    var wrap = function (h) {
        return recur(function (f) {
            var g = function (n) {
                return f(f)(n);
            };
    
            return h(g);
        });
    };
    
    var fact = wrap(function (g) {
        return function (n) {
            if (n < 2) return 1;
            return n * g(n - 1);
        };
    });
     

    Step 6

    g只调用了一次,让我们把g内联进wrap里。

    var recur = function (f) {
        return f(f);
    };
    
    var wrap = function (h) {
        return recur(function (f) {
            return h(function (n) {
                return f(f)(n);
            });
        });
    };
    
    var fact = wrap(function (g) {
        return function (n) {
            if (n < 2) return 1;
            return n * g(n - 1);
        };
    });
     

    Step 7

    现在,如果我们把recur也内联进wrap里,我们就得到了著名的Y结合子

    var Y = function (h) {
        return (function (f) {
            return f(f);
        })(function (f) {
            return h(function (n) {
                return f(f)(n);
            });
        });
    };
    
    var fact = Y(function (g) {
        return function (n) {
            if (n < 2) return 1;
            return n * g(n - 1);
        };
    });


    The End

    玩的开心!

  • 相关阅读:
    python——numpy模块
    python——xlrd、xlwt、xlutils模块
    python——json&pickle模块
    python——sys模块
    python——os模块
    python——random模块
    python——time模块
    linux命令 pwd
    linux 里面ls命令!!
    校花网图片爬取
  • 原文地址:https://www.cnblogs.com/windydays/p/2439519.html
Copyright © 2011-2022 走看看