浅析函数式编程

浅析函数式编程

以函数抽象为单元


    function parseAge(age) {
        if (!_.isString(agr)) {
            throw new Error('Expecting is String');
        }

        console.log('告警:接收到参数,准备类型转化');

        var temp = parseInt(age, 10);

        if (_.isNaN(temp)) {
            console.log(['不能格式转化为数字', age].join(''));
        }

        return temp;
    }


    function fail(thing) {
        throw new Error(thing);
    }

    function warn(thing) {
        console.log(['告警:', thing].join(''));
    }

    function note(thing) {
        console.log(['提示:', thing].join(''));
    }

    function parseAge(age) {
        if (!_.isString(agr)) {
            fail('Expecting is String');
        }

        note('接收到参数,准备类型转化');

        var temp = parseInt(age, 10);

        if (_.isNaN(temp)) {
            warn(['不能格式转化为数字', age].join(''));
        }

        return temp;
    }

以函数行为为单位


    function naiveNth(data, index) {
        if (!_.isNumber(index)) {
            fail('参数不是一个数字');
        }
        if (!(_.isArray(data) || _.isString(data))) {
            fail('data, 不支持下标取值');
        }

        if (index < 0 || index > data.length) {
            fail('下标溢出');
        }

        return data[index];
    }


    function isIndex (data){
        return _.isArray(data) || _.isString(data);
    }

    function naiveNth(data, index) {
        if (!_.isNumber(index)) {
            fail('参数不是一个数字');
        }
        if (!isIndex(data)) {
            fail('data, 不支持下标取值');
        }

        if (index < 0 || index > data.length) {
            fail('下标溢出');
        }

        return data[index];
    }

  • 案例2: 排序功能

    // 常规实现
    [2, 3, -1, -6, 0, -108, 42, 10].sort(function compareLessThanOrEqual(x, y) {
        if (x < y) return -1;
        if (y < x) return 1;
        return 0;
    });


    // 函数编程进阶一
    function compareLessThanOrEqual(x, y) {
        if (x < y) return -1;
        if (y < x) return 1;
        return 0;
    }

    [2, 3, -1, -6, 0, -108, 42, 10].sort(compareLessThanOrEqual);


    // 函数编程进阶二
    function lessOrEqual(x, y) {
        return x <= y;
    }

    // 一个比较器的高阶函数
    function comparator(pred) {
        return function(x, y) {
            if (pred(x, y))
                return -1;
            else if (pred(y, x))
                return 1;
            else
                return 0;
        };
    };

    [2, 3, -1, -6, 0, -108, 42, 10].sort(comparator(lessOrEqual))

命令式编程

  • 通过描述详细行为的编程方式

  • 案例:

    // 开始数字 99 
    // 重复以下内容到数字为1
    // ---- 有x瓶啤酒
    // ---- 拿走一个
    // ---- 还有X - 1 个

    // 最后的数字减1,下一个回合
    // X为1的时候,输出没有啤酒

    // 命令式编程风格
    var lyrics = [];
    for (var bottles = 99; bottles > 0; bottles--) {
        lyrics.push(bottles + " bottles of beer on the wall");
        lyrics.push(bottles + " bottles of beer");
        lyrics.push("Take one down, pass it around");

        if (bottles > 1) {
            lyrics.push((bottles - 1) + " bottles of beer on the wall.");
        } else {
            lyrics.push("No more bottles of beer on the wall!");
        }
    }

    function lyricSegment(n) {
    return _.chain([])
        .push(n + " bottles of beer on the wall")
        .push(n + " bottles of beer")
        .push("Take one down, pass it around")
        .tap(function(lyrics) {
            if (n > 1)
                lyrics.push((n - 1) + " bottles of beer on the wall.");
            else
                lyrics.push("No more bottles of beer on the wall!");
            })
        .value();
    }
    
    function song(start, end, lyricGen) {
        return _.reduce(_.range(start, end, -1), function(acc, n) {
            return acc.concat(lyricGen(n));
        }, []);
    }

    song(99, 0, lyricSegment)

实现高阶函数

  • 核心一: 使用函数而不是值

    // 实现一个复制函数,参数为一个数字和一个值,将值多次复制,放入一个数组之中 
    function repeat(times, VALUE) {
        return _.map(_.range(times), function() { return VALUE; });
    }

    // 缺点 变量Value写死,不易拓展
  • 稍微修改提高函数等级
    function repeatedly(times, fun) {
        return _.map(_.range(times), fun);
    }

    // eg:
    repeatedly(3, function() {
        return Math.floor((Math.random()*10)+1);
    });
    //=> [1, 3, 8]

    repeatedly(3, function(n) {
        document.getElementById('id' + n);
    });

    // 确定 复制次数是提前知道的
  • 再次升级
    function iterateUntil(fun, check, init) {
        var ret = [];
        var result = fun(init);

        while (check(result)) {
            ret.push(result);
            result = fun(result);
        }

        return ret;
    };
    // 优点:终止条件由函数决定

    // 找到 1024 内所有 2的幂次方数
    iterateUntil(
        function() { return n + n },
        function() { return n <= 1024; },
        1
    )

    repeatedly(10, function(exp) { Math.pow(2, exp + 1) })


    function invoker(NAME, METHOD) {
        return function(target /* args ... */ ) {
            if (!existy(target)) fail("Must provide a target");

            var targetMethod = target[NAME];
            var args = _.rest(arguments);

            return doWhen((existy(targetMethod) && METHOD === targetMethod), function() {
                return targetMethod.apply(target, args);
            });
        };
    };

函数柯里化

  • 给定某个目标的方法调用被推迟到参数个数耗尽

   // 接收一个函数,返回只接受一个参数的函数 
    function curry(fun) {
        return function(arg) {
            return fun(arg);
        };
    }

    // 看起来很多余,主要是是想解决js函数多余的参数

    // eg: 
    ['11', '11', '11', '11'].map(parseInt);
    ['11', '11', '11', '11'].map(curry(parseInt);

    function curry2(fun) {
        return function(secondArg) {
            return function(firstArg) {
                return fun(firstArg, secondArg);
            };
        };
    }
    var parseBinaryString = curry2(parseInt)(2);

    parseBinaryString("111");

更好用的部分柯里化


    function partial1(fun, arg1) {
        return function( /* args */ ) {
            var args = construct(arg1, arguments);
            return fun.apply(fun, args);
        };
    }

    function partial2(fun, arg1, arg2) {
        return function( /* args */ ) {
            var args = cat([arg1, arg2], arguments);
            return fun.apply(fun, args);
        };
    }

    // eg
    if (!Function.prototype.bind)(function() {
        var slice = Array.prototype.slice;
        Function.prototype.bind = function() {
            var thatFunc = this,
                thatArg = arguments[0];
            var args = slice.call(arguments, 1);
            return function() {
                var funcArgs = args.concat(slice.call(arguments))
                return thatFunc.apply(thatArg, funcArgs);
            };
        };
    })();

总结

  • 函数式编程主要包含以下几点
  1. 确定抽象,并为其构建函数
  2. 利用已有的函数构建更为复杂的抽象
  3. 通过现有的函数传给其他的函数来构建更为复杂的函数
  • 高阶函数
  1. 以一个函数作为参数
  2. 以一个函数作为返回结果

Lodash
Underscore
gitHub

你可能感兴趣的:(JavaScript)