函数式编程:柯理化函数和组合函数

柯理化函数和组合函数都归属于函数式编程,用于解决函数式编程的问题

首先先说明一下函数式编程和面向对象式编程的优缺点
* 面向对象编程的
    优点
        程序更加便于分析、设计、理解
        是易拓展的,由于继承、封装、多态的特性,
        自然设计出高内聚、低耦合的系统结构,使得系统更灵活、更容易扩展,而且成本较低
    缺点
        为了写可重用的代码而产生了很多无用的代码,导致代码膨胀

* 函数式编程的
    优点
        代码可读性更强。
        实现同样的功能函数式编程所需要的代码比面向对象编程要少很多,
        代码更加简洁明晰
        开发速度快
    缺点
        所有的变量在程序运行期间都是一直存在的,非常占用运行资源

柯理化函数currying

  • 概念:是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

示例:

//普通函数
function add(n1, n2) {
    return n1 + n2
}
add(1, 2)


//柯理化函数
function add(n1) {//把参数一个一个的传入
    return function (n2) {//返回带着返回结果的新函数
        return n1 + n2
    }
}
add(1)(2);
  • 为什么使用柯理化函数
    柯理化就是利用模块化思想处理多参函数,通过组合函数减少每个函数的入参数量,从而提高代码的可阅读性及可维护性。

创建一个柯理化函数

function currying(fun) {
    var args = Array.prototype.slice.call(arguments, 1);
    return function () {
        var _args = args.concat(Array.prototype.slice.call(arguments));
        return fun.apply(null, _args);
    }
}

利用柯理化函数改造一个函数

function add(...vals){
    return vals.reduce((pre, val) = > {
            return pre + val;
    });
}

var newAdd = currying(add, 1, 2, 3);
console.log(newAdd(4, 5, 6)); // 21

但是上边封装的柯理化函数的代码,只能使用一次,不能够多次执行

function currying(fn) {
    var args = [].slice.call(arguments, 1);
    return function () {
        if (arguments.length == 0) {
            return fn.apply(null, args);//如果不带参数调用,就说明参数已经传完了,要返回结果了
        } else {
            args = args.concat([].slice.call(arguments));//如果调用时带着参数,只需要把参数添加进去,利用不销毁作用域,下次再调用的时候就包含上次传递的参数了
        }
    }
}

多次调用

function add() {
    var vals = Array.prototype.slice.call(arguments);
    return vals.reduce((pre, val) = > {
            return pre + val;
    });
}
var newAdd = currying(add, 1, 2, 3);
newAdd(4, 5);
newAdd(6, 7);
console.log(newAdd());  // 28
//把每次函数调用的参数都存储起来,如果已无参形式调用,说明记录结束,需要做最终计算。

高级柯理化函数

function sum(a, b, c) {
  return a + b + c;
}
sum(1,2,3)
//想实现一个柯理化函数
let curried= curry(sum);
curried(1,2,3) //6
curried(1)(2,3) //6
curried(1)(2)(3) //6
function curry(func) {
  return function curried(...args) {
    if (args.length >= func.length) {
      return func.apply(this, args);
    } else {
      return function(...args2) {
        return curried.apply(this, args.concat(args2));
      }
    }
  };
}
//如果传入的参数个数的和一直小于原函数的个数就递归执行,直到参数全部执行完就调用原函数

偏函数和柯理化函数的概念及区别

  • 当把已知函数的一些参数固定,结果函数被称为偏函数,通过使用bind获得偏函数,也有其他方式实现。
    eg: 当我们不想一次一次重复相同的参数时,偏函数是很便捷的。如我们有send(from,to)函数,如果from总是相同的,可以使用偏函数简化调用。
  • 柯里化是转换函数调用从f(a,b,c)至f(a)(b)(c).Javascript通常既实现正常调用,也实现参数数量不足时的偏函数方式调用。

组合函数compose

简单来说,就是把很多函数组合到一起,执行一个函数,所有的函数都执行了

  • 将函数串联起来执行,将多个函数组合起来,一个函数的输出结果是另一个函数的输入参数,一旦第一个函数开始执行,就会像多米诺骨牌一样推导执行了。
  • 特点
    compose的参数是函数,返回的也是一个函数
    因为除了第一个函数的接受参数,其他函数的接受参数都是上一个函数的返回值
    compsoe函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左的,初始函数一定放到参数的最右面

示例

var greeting = (firstName, lastName) =>'hello,  ' + firstName + '  ' + lastName + '   ';
var toUpper = str =>str.toUpperCase();
var fn = compose(toUpper, greeting);//从右向左执行
//只有greeting接受参数'jack', 'smith',toUpper 接受的参数是greeting的返回值
console.log(fn('jack', 'smith'))// ‘HELLO,JACK SMITH’

我使用的reduce实现的compose

function compose(){
   let args=[].slice.call(arguments);
   var length = args.length;
   var index = length;
   while (index--) {//如果参数中存在不是函数类型的值,就抛出错误
        if (typeof args[index] !== 'function') {
            throw new TypeError('Expected a function');
        }
    }
   let last=args.pop();
   args=args.reverse();
   return function (){
       let _args = [].slice.call(arguments);
       return args.reduce((prev, next)=>{
          return next(prev);//每一个函数执行的返回值都作为上一个函数的参数传进去
       },last.apply(null,_args))//最后一个函数是需要传值的
   }
}

继续执行

var trim = str =>str.replace(/\s+/g, '|');
var newFn = compose(trim, fn);
console.log(newFn('jack', 'smith'));
  • loadsh的flow / flowRight实现 (利用while循环)
    flow 返回一个函数,连续调用参数中传递的函数数组
    //lodash的实现 从左向右执行的函数
var flow = function (funcs) {
    var length = funcs.length;
    var index = length
    while (index--) {
        if (typeof funcs[index] !== 'function') {
            throw new TypeError('Expected a function');
        }
    }
    return function (...args){
        var index = 0
        var result = length ? funcs[index].apply(this, args) : args[0]
        while (++index < length) {
            result = funcs[index].call(this, result)
        }
        return result
    }
}
var flowRight = function (funcs) {
    return flow(funcs.reverse())
}

你可能感兴趣的:(函数式编程:柯理化函数和组合函数)