函数柯里化

函数柯里化

Posted by limantang on November 13, 2019

函数柯里化

函数柯里化

什么是函数柯里化呢, 简单来说就是所有的函数都只接受一个参数 那么基于函数接受单一参数有什么实际的意义呢, 其实这个作用还是很大的, 而且已经衍生出了很多的理论知识, 感兴趣的同学可以google了解一下

本文谈论一下如何柯里化一个函数

前面说了, 函数柯里化就是函数接受一个参数, 如果接受一个参数呢?

  1. 我第一次接触的时候是这样想的, 不就是要接受一个参数吗, 可不可以这样做呢 假设原函数是这样的, 我要改装成柯里化之后的样子
    function fn (a, b) {
     return a + b;
    }
    fn(1,2)
    

    可以改成这样

    function curriedFn({a,b}) {
     return a + b;
    }
    curriedFn({1,2})
    

    这种做法是可以的, 使用对象接受单一参数, 符合柯里化的要求, 但是很蠢😀

  2. 之前说过, 对象是穷人的闭包, 其实可以不用对象去实现, 而是去闭包
    const add = a => b => a + b;
    add(1)(2);	
    

    上面是箭头函数的形式, 下面是普通函数的形式

    const add = function (a) {
     return function (b) {
         return a + b;
     }
    }
    add(1)(2);
    

    实现的功能是一样的, 箭头函数看起来更优雅一些

柯里化题目

如何将add(1, 2, 3)变成curriedAdd(1)(2)(3)的形式

const curriedAdd =
	a => 
		b => 
			c => 
				add(a, b, c)

解答完毕

来一个升级版的题目 假设addTwo 接受两个参数 addThree 接受三个参数 addFour 接受四个参数 编写一个currify函数, 使它们分别接受2, 3, 4次参数

例如

currify(addTwo)(1)(2)		//	3
currify(addThree)(1)(2)(3)		//	6
currify(addFour)(1)(2)(3)(4)		//	10

也就是说, currify可以将一个接受固定参数个数的函数, 编程接受单一参数的函数 首先分析一下问题, 这个currify函数接受一个函数作为参数, 由于可以多次调用, 那么说明返回值也是函数, 如果是最后一次调用, 那么就是函数执行然后输出结果 确定了输入输出, 这个currify函数就好实现一些了, 如何确定应该是返回一个函数, 还是执行一个函数呢, 这个经过分析也不难想到, 应该是当参数个数等于被柯里化函数的形参长度时就可以执行函数了, 否则就是不停地返回一个函数 在没有最终的函数执行之前, 也就是在不断的返回函数的过程中, 一定要把每一次输入的单参数记录下来, 为了最后一次函数执行时可以获取到完整的参数 在不断返回函数的过程中其实也是一个递归的过程, 下面给出实现, 大家参考上面分析的过程理解一下这个函数

function currify(fn, params = []) {
	return function (arg) {
		params.push(arg)
		if (params.length === fn.length) {
			return fn(...params);
		} else {
			return currify(fn, [...params])
		}
	}
}
function add(a,b,c,d){
	return a + b + c + d;
}
currify(add)(1)(2)(3)(4)

这就是完整的实现, 其中有几个需要注意的点 为什么要有一个params这个数组呢, 这个数组就是为了把所有的单个参数都记录下来, 每一次递归的调用currify(fn, [...params])是为了继续可以返回一个函数, 而且还可以将所有的单个参数记录下来, 直到params.length === fn.length

下面进行一个小升级可以接受这种形式 currify(add)(1,2)(3)(4)

function currify(fn, params = []) {
	return function (...args) {
		if (args.length + params.length === fn.length) {
			return fn(...params, ...args);
		} else {
			return currify(fn, [...params, ...args])
		}
	}
}
function add(a,b,c,d){
	return a + b + c + d;
}
currify(add)(1, 2)(3)(4)

改用剪头函数实现更为简洁

currify = (fn, params = []) => 
	(...args) => 
		args.length + params.length === fn.length ?
		fn(...params, ...args) :
		currify(fn, [...params, ...args])
const add = (a,b,c,d) => a + b + c + d;

currify(add)(1, 2)(3)(4)

函数柯里化也是递归函数一种应用