函数compose和柯理化的代码实现

395 阅读3分钟

函数柯理化

有时候会遇到类似于这种需求

add(5)(2)(3)=10 add(2,5)(3)=10 add(1)(7,2)=10

add(1)(8)(1)()=10 add(7,1)(1)(1)()=10 add(4)(2,1)(1)(2)()=10


柯理化的两种写法

一、传入的参数个数满足函数执行的参数个数,即开始执行函数

function curry_(fn) {
 // 返回一个新函数judge,接收参数为 ...args
	return function judge(...arg) {
		if (arg.length >= fn.length) {
                //新函数接收的参数长度是否满足函数执行需要接收的长度
				return fn(...arg)
                      // 满足要求,执行 fn 函数,传入新函数的参数
				} else {
                     // 不满足要求,递归 judge 函数 整合下一个执行所传递的参数
				return function(...args) {
					return judge(...args, ...arg)
			}
		}
	}
}

var curry_fn = curry_(function(a, b, c) {
	return a + b + c
});
console.log(curry_fn(5)(2)(3), '有限参数柯理化');
//10

es6简化:
    const curry_ = fn =>judge = (...args) =>args.length === fn.length ?fn(...args) : (...arg) => judge(...args, ...arg)

二、传入的参数最后一个拿到的参数值为空,即开始执行函数

function curry_(fn) {
	return function judge(...arg) {
		//返回新函数,此时arg为[23, 1],即curry_fn传入的第一个参数
		return function(...args) {
			//返回每次执行前一个函数之后的函数即curry_fn(23, 1)执行之后的函数,args为3、4、0
			if (!args.length) {
				//如果后一个函数没有参数即为(),则开始执行函数
				return fn(...arg)
			} else {
				//如果后一个函数的参数存在,则进行递归,存入arg中
				return judge(...args, ...arg);
			}
		}
	}
}

var curry_fn = curry_(function(...args) {
	return args.reduce((a, b) => a + b, 0)
});
console.log(curry_fn(2, 1)(3)(4)(), '有限参数柯理化');
//10

es6简化:
    const adder_ = fn => judge = (...arg) => (...args) => !args.length ? fn(...arg) : judge(...arg, ...args)

compose 函数

1、从右向左以此执行 2、且上一个函数的执行结果作为下一个函数的参数 3、第一个函数是多参数,后面的函数都是一个参数 4、所有函数的执行都是同步的

let greeting = (...arg) => 'hello, ' + arg.join(' ')//第一个函数(多参数)
let toUpper = str => str.toUpperCase()//第二个函数
let timing = str => `${str}  time= ${+new Date()}`//第三个函数

//方法一、reduce法

let compose = function(...func) {
	//如果没有函数,则返回compose_my函数中第一个参数
	if (func.length == 0) {
		return function(arg) {
			return arg
		}
	}
	//如果只有一个函数,则返回返回这个函数
	if (func.length == 1) {
		return func[0]
	}
	//返回reduce处理的结果
	return func.reduce((itemFn, callback) => {
		//初始callback为第一个函数即 greeting()
		return function(...arg) {
			//arg为接受处理函数返回的最终函数的参数 即'jack', 'smith', 'wang'
			return itemFn(callback(...arg))
			//第一个函数接受多个参数,返回一个值被下一个函数作为参数,最终输出
		}
	})
}

//es6版:
    let compose = (...funcs) => {
        if (funcs.length === 0) return arg => arg
        if (funcs.length === 1) return funcs[0]
        return funcs.reduce((a, b) => (...args) => a(b(...args)))
    }


//方法二、滑块递归法
let compose = (...args) => {
	var len = args.length
    // 记录我们传入所有函数的个数
	var count = len - 1
    // 游标记录函数执行情况, 也作为我们运行中函数的索引
	var result
    // 结果, 每次函数执行完成后, 向下传递
	return function f1(...arg) {
		//arg为接受处理函数返回的最终函数的参数 即'jack', 'smith', 'wang'
		result = args[count](...arg)
		if (count <= 0) {
        //只有一个函数时
			count = len - 1
			return result
		} else {
        //多个函数时,游标移动,递归执行
			count--
			return f1(result)
		}
	}
}

let fn = compose(timing, toUpper, greeting)
console.log(fn('jack', 'smith', 'wang'))