浅谈函数柯里化

函数柯里化就是将接收多个参数的函数转变为一系列接收单一参数的函数,而且返回接收余下参数的函数。

柯里化是什么

柯里化, 即 Currying 的音译。Currying 是编译原理层面实现多参函数的一个技术。javascript

咱们在编码的过程当中,本质工做就是将复杂的问题分解成多个可编程的小问题。java

Currying 就是将接收多个参数的函数转变为一系列接收单一参数的函数,而且返回接收余下参数的函数。git

举例来讲:github

//一个接收4个参数的函数
let demo = function(a,b,c,d){
    console.log([a,b,c,d]);
}

//假设咱们有一个转换函数 curry

//生成一个柯里化函数
let _demo = curry(demo);

_demo(1)(2)(3)(4); // [1,2,3,4]

须要注意的是,在编译原理层面的柯里化,一次只能传递给函数一个参数;而咱们在编程过程当中实际使用的柯里化函数,是能够传递给函数一个或多个参数。ajax

仍是刚才的例子:编程

//一个接收4个参数的函数
let fn = function(a,b,c,d){
    console.log([a,b,c,d]);
}

//假设咱们有一个转换函数 curry

//生成一个柯里化函数
let _fn = curry(fn);

_fn(1)(2)(3)(4); // [1,2,3,4]
_fn(1,2)(3,4); // [1,2,3,4]
_fn(1)(2,3,4); // [1,2,3,4]

当咱们知道柯里化是什么了的时候,咱们来看看柯里化到底有什么用?数组

用途

来看一个例子:app

// 示意而已
function ajax(type, url, data) {
    var xhr = new XMLHttpRequest();
    xhr.open(type, url, true);
    xhr.send(data);
}

// 虽然 ajax 这个函数很是通用,但在重复调用的时候参数冗余
ajax('POST', 'www.test.com', "name=kevin")
ajax('POST', 'www.test2.com', "name=kevin")
ajax('POST', 'www.test3.com', "name=kevin")

// 利用 curry
var ajaxCurry = curry(ajax);

// 以 POST 类型请求数据
var post = ajaxCurry('POST');
post('www.test.com', "name=kevin");

// 以 POST 类型请求来自于 www.test.com 的数据
var postFromTest = post('www.test.com');
postFromTest("name=kevin");

curry 的这种用途能够理解为:参数复用。本质上是下降通用性,提升适用性。函数

但是即使如此,是否是依然感受没什么用呢?工具

再来看一个例子:

假设咱们有这样一段数据:

var personList = [{name: 'bingshan'}, {name: 'group'}]

如今咱们须要将 personList 中的全部 name 值提取出来,放入一个变量 names 中。

一般状况下,咱们会这样来实现:

let names = list.map(function(item) {
  return item.name;
})

然而,使用柯里化的思想,咱们会这样实现:

var prop = curry(function(key,obj){
    return obj[key];
})
var names = person.map(prop('name'));

看到这个例子,可能不少人会说,使用柯里化的思想以后,变得更麻烦了。

其实不是的,prop 函数只须要建立一次,之后每次获取数组对象中的元素的时候,均可以拿来直接使用的,咱们是能够将其看作是一个工具函数,与咱们的业务代码无关。

那么此时,咱们的业务代码就只剩一行了,而且看起来比传统书写方式更加直观易懂

var names = person.map(prop('name'));

实现

接下来,咱们来思考如何实现 curry 函数。

回想以前咱们对于柯里化的定义,接收一部分参数,返回一个函数接收剩余参数,接收足够参数后,执行原函数。

function curry(fn,...params){
    return function(...args){
        let _args = [...params,...args];
        if(_args.length >= fn.length){
            return fn.apply(this,_args);
        }else{
            return curry.call(this,fn,..._args);
        }
    }
}

验证一下:

//一个接收4个参数的函数
let demo = function(a,b,c,d){
    console.log([a,b,c,d]);
}

let _demo = curry(demo);

_demo(1,2,3,4);         // print: [1,2,3,4]
_demo(1)(2)(3,4);       // print: [1,2,3,4]
_demo(1,2)(3,4);        // print: [1,2,3,4]
_demo(1)(2)(3)(4);      // print: [1,2,3,4]

优化

咱们以前完成的 curry 函数,依赖与原函数的形参个数,只有接受的总参数个数大于等于原函数的形参个数时,才会调用原函数。

那么若是咱们但愿传入的参数个数在不等于原函数的形参个数的时候,原函数依然能执行,咱们该怎么作呢?很简单,将 curry 函数修改一下:

function curry(fn,len = fn.length,...params){
    return function(...args){
        let _args = [...params,...args];
        if(_args.length >= len){
            return fn.apply(this,_args);
        }else{
            return curry.call(this,fn,len,..._args);
        }
    }
}

再来验证一下:

//一个接收4个参数的函数
let demo = function(a,b,c,d){
    console.log([a,b,c,d]);
}

//接收三个参数后,执行原函数
let _demo = curry(demo,3);

_demo(1)(2)(3); // [1,2,3,undefined]

扩展

对于咱们经常使用的工具函数库 lodash 不只提供了 curry 工具函数,还提供了参数占位符的玩法:

import _ from 'lodash'
let demo = function(a,b,c,d){
    console.log([a,b,c,d]);
}
let _demo = _.curry(demo);
_demo(_,2)(1,_,4)(3) // [1,2,3,4]

参考连接

相关文章
相关标签/搜索