Skip to main content

函数式编程之柯里化

Currying

接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术

固定一个可以预期的参数,并返回一个特定的函数,处理批特定的需求

关键词:参数复用 返回一个特定函数 返回的函数接收余下的参数 批处理 延迟运行

预习

call 、 apply区别

通用实现方法

const currying = (fn, ...args) => (...innerArgs) => fn.apply(null, args.concat(innerArgs))

特点

降低适用范围、提高适用性

例子: 重复的 处理一组数据中的每个元素

const arrA = [1, 2, 3]
const arrB = [4, 5]
const arrC = [6, 7, 8]

// 处理成 自乘,加倍

解决方案:

  1. 通用函数
// 自乘
const square = i => i * i
// 加倍
const double = i => i * 2
// 通用函数
const map = (handler, list) => list.map(handler)

map(square, arrA)
map(square, arrB)
map(square, arrC)

map(double, arrA)
map(double, arrB)
map(double, arrC)

通用函数,对通用性增强,带来了对适用性的减弱

  1. 柯里化
const squareCurry = currying(map, square)

squareCurry(arrA)
squareCurry(arrB)
squareCurry(arrC)

const doubleCurry = currying(map, double)

doubleCurry(arrA)
doubleCurry(arrB)
doubleCurry(arrC)

延迟执行

不断的柯里化,累积传入的参数,最后才输出

例子:

add(1)(2) // => 3

const add = (pre) => (next) => pre + next
add(1)(5)(8)(6)(3)(3)(7)()

function add (num) {
let sum = num
return function callback (innerNum) {
if (!arguments.length) {
return sum
}
sum = sum += innerNum

return callback
}
}

console.log(add(1)(2)(3)())

使用柯里化,通用写法

const currying = (fn) => {
const args = []

return function callBack() {
if (arguments.length == 0) {
// 需要在这里考虑执行环境
return fn.apply(this, args)
}
Array.prototype.push.apply(args, arguments);

return callBack;
}
}

const sum = (...nums) => {
let result = 0
nums.forEach(num => result += num)

return result
}

const sumCurrying = currying(sum)(1)(2)(3)

console.log(sumCurrying())

拓展之反柯里化

非我之物,为我所用

创建一个更普适性的函数,可以被不同的对象使用

提高适用范围,降低适用性

与我无关的展示方法,使用它去展示我

function Toast (option) {
this.prompt = ''
}
Toast.prototype = {
constructor: Toast,
// 输出提示
show: function () {
console.log(this.prompt)
}
}

// 新对象
var obj = {
prompt: '新对象'
};
function unCurrying(fn) {
return function (arg, ...other) {
return fn.apply(arg, other)
}
}

var objShow = unCurrying(Toast.prototype.show)

objShow(obj)

通用unCurrying

Function.prototype.unCurrying = function () {
const self = this

return function () {
return Function.prototype.call.apply(self, arguments)

// Function.prototype.call.apply
// 可以理解为 --> callFunction.apply()
// 将callFunction的this指针,通过apply指向self,并传入参数arguments
// callFunction(arguments)
// --> callFunction(arg[0], arg[1-n])


// Function.prototype.call.apply(Toast.prototype.show, [obj])

// -->
// Toast.prototype.show.call(obj)

// -->
// obj.show()
}
}
const objShow = Toast.prototype.show.unCurrying()
objShow(obj)
const slice = Array.prototype.slice.unCurring()
slice(arguments)

函数式编程高阶应用

  • 范畴论

函数式编程的起源,是一门范畴论的数学分支

"范畴就是使用箭头连接的物体。"

(In mathematics, a category is an algebraic structure that comprises "objects" that are linked by "arrows". )

  • Effect 函子
  • of 方法
  • Maybe函子
  • Either函子
  • ap函子
  • Monad函子
  • 将不纯的I/O(输入/输出)操作写成Monad函子来完成