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

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

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

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

柯理化函数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())
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容