乐趣区

关于javascript:函数compose和柯理化的实现

函数柯理化

有时候会遇到相似于这种需要

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'))
退出移动版