柯里化

140 阅读2分钟

 是什么

维基百科

计算机科学中,柯里化(英语:Currying),又译为卡瑞化加里化,是把接受多个参数函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。

并且返回接受余下的参数而且返回结果的新函数的技术,这个新函数可以接受余下参数而且返回结果

这个技术由克里斯托弗·斯特雷奇以逻辑学家哈斯凯尔·加里命名的,尽管它是Moses Schönfinkel戈特洛布·弗雷格发明的

学习资料推荐

通过函数调用继续返回函数的形式,实现多次接收参数最后统一处理的函数编码形式!

1. 怎么用

参数复用

普通写法

function show(_school, _class, _name) {
    return `学校:${_school},班级:${_class},姓名:${_name}`
}
show('王者', '2 班', '吕布')
show('王者', '2 班', '赵云')
show('王者', '2 班', '典韦')

柯里化后

function show(_school) {
    return function (_class) {
        return function (_name) {
            return `学校:${_school},班级:${_class},姓名:${_name}`
        }
    }
}
const f = show('王者')('2 班')
f('吕布')
f('赵云')
f('典韦')

再举个例子

function show(protocol, domain, port, path) {
    return `${protocol}${domain}:${port}${path}`
}
show('https://', 'www.baidu.com', 80, '/home')
show('https://', 'www.baidu.com', 80, '/news')
show('https://', 'www.baidu.com', 80, '/sports')

2. 转柯里

function show(_school, _class, _name) {
    return `学校:${_school},班级:${_class},姓名:${_name}`
}

const curry = (fn) => {
    // 要被柯里化的参数的个数
    const len = fn.length
    // 第 1 次肯定返回的是一个新函数
    return function temp() {
        const args = [...arguments]
        // 新函数调用完之后到底是执行 fn 还是继续返回新函数呢,取决于收集到的参数个数和 fn.length 的关系
        if (args.length >= len) {
            return fn(...args)
        } else {
            return function () {
                return temp(...args, ...arguments)
            }
        }
    }
}

const curryShow = curry(show)
// const r = curryShow('王者', '2018 ', '吕布')
// const r = curryShow('王者')('2018 ', '吕布')
const r = curryShow('王者')('2018 ')('吕布')
console.log(r)

3. 鲁大师

const lodash = require('lodash')
function show(_school, _class, _name) {
    return `学校:${_school},班级:${_class},姓名:${_name}`
}

const curryShow = lodash.curry(show)
// const r = curryShow('王者', '64', '吕布')
// const r = curryShow('王者')('64', '吕布')
const r = curryShow('王者')('64')('吕布')
console.log(r)

4. 面试题

add(1)(2)(3)(4) => 10
add(1)(2)(3)(4, 5) => 15
add(1)(2)(3)(4)(5) => 15

**意思:**并不是说 add(1)(2)(3)(4) 的返回值是 10,而是 add(1)(2)(3)(4) 参与运算后当做 10 来用

**铺垫:**对象(数组、对象、函数)参与运算,会调用 toString 或 valueOf 得到一个原始值,然后用这个原始值参与运算!

// 对象参与运算,会使用 valueOf 返回的结果进行;如果 valueOf 返回的不是 primitive value,则使用 toString
const obj = {
    valueOf() {
        return []
    },
    toString() {
        return 8
    },
}
console.log(obj + 3)

**思路:**能够持续调用,必然返回函数,能够在参与运算时当成数值,那只能通过 valueOf 或 toString 方法来实现!

function add() {
    // !第一个函数里的参数
    let args = Array.prototype.slice.call(arguments)
    let inner = function () {
        // !把第二个函数的参数加到第一个括号里面
        args.push(...arguments)
        return inner
    }
    inner.toString = () => args.reduce((calc, cur) => calc + cur)
    return inner
}

console.log(add(1)(2)(3)(4) == 10)
console.log(add(1)(2)(3)(4, 5) == 15)
console.log(add(1)(2)(3)(4)(5) == 15)