函数式编程(Functional Programming)之不完全函数(Partial Function)

近来,我对FP的想法由之前的反感(看着Lisp风格的代码花花绿绿的函数调用嵌套简直了。。)开始有了转变,譬如,我就对其中的Partial Function产生了浓厚的兴趣。

我的个人想法,Partial Function的产生更像是柯里化(Currying)的高阶函数变种,这种编程思想是将函数调用时接收实参分成好几个阶段,每次调用都会返回一个新函数,即把一次函数调用拆成多次函数调用,每次传入的实参都是完整实参的一部分,举个例子,即把f(1,2,3,4,5,6)的调用拆成f(1,2)(3,4)(5,6)。这种实现方式曾经在lodash函数库见过,大家有兴趣可以去八卦。

比较典型的是,我曾经在红宝书看Nicholas C.Zakas提到的柯里化处理方式:

function Curry(fn){
     var args=Array.prototype.slice.apply(arguments,[1]);  // 接收第一批实参
     // 返回闭包
     return function(){
         var innerArgs=Array.prototype.slice.apply(arguments);  // 接收第二批实参
         var finalArgs=args.concat(innerArgs);    // 拼接
         return fn.apply(null,finalArgs);           // 将拼接后的参数数组送给fn函数调用
     };
}

function add(num1,num2){
     return num1+num2;
}

var curriedAdd=Curry(add,2);

console.log(curriedAdd(3));   // 5

这种处理方式可以将每次分批接收的实参拼接起来一起传给指定函数去调用,来看一下Partial Function是怎样实现的:

function part(fn){
    var args=Array.prototype.slice.apply(arguments,[1]);  // 第一批接收的实参
    return function(){
        var inArgs=Array.prototype.slice.apply(arguments); // 第二批接收的实参
        return function(){
            var innerArgs=Array.prototype.slice.apply(arguments);  //第三批
            var finalArgs=args.concat(inArgs.concat(innerArgs));  // 拼接
            return fn.apply(null,finalArgs);
        };
    };
}

function add(a,b,c,d,e){
    return a+b+c+d+e;
}

console.log(part(add,1)(2,3)(4,5));   // 15

除了最后一次调用,每一次调用都会返回一个闭包,比较神奇的是,这种调用可以选择不同的调用实参来实现不同的效果:

// 将类数组对象转数组的代码封装成一个函数方便转换
function toArray(a,n){
    return Array.prototype.slice.apply(a,[n||0]);
}

function left(fn){
    var args=arguments;   // args指向外部函数的arguments对象以待转换
    return function(){
        var array1=toArray(args,1);   // 处理args转换成数组
        var array2=array1.concat(toArray(arguments));  // 拼接
        return fn.apply(null,array2);
    };
}

function right(fn){
    var args=arguments;
    return function(){
        var array1=toArray(arguments);   // 注意这里与left函数的不同
        var array2=array1.concat(toArray(args,1));
        return fn.apply(null,array2);
    };
}

function calc(a,b,c){
    return a*(b-c);
}

console.log(left(calc,2)(3,4));  // -2, 计算的是2*(3-4)

console.log(right(calc,2)(3,4));  // 6, 计算的是3*(4-2)

通过闭包来控制实参数组的顺序,可以实现神奇的效果。

总的来说,通过返回闭包来进行多次调用在一些JS库中也是能够见到它们的身影,但个人认为,像这种类似柯里化的行为我觉得在工程上是比较华而不实的东西(国外那些搞软件工程的人呀。。。非要搞个玄乎的名词出来恶心别人),说到底就是炫技。柯里化是因为lambda演算只有一个参数才被发明的,除了恶心自己还要恶心别人,唉。。。。

posted @ 2018-03-15 19:47  linweiws  阅读(776)  评论(0编辑  收藏  举报