文章目录
1. 一元函数、多元函数和变参函数
一元函数、多元函数
var fx = function(x) {
return x * x
}
var fxyz = function(x, y, z) {
return x + y + fx(z)
}
console.log(fx(2))
console.log(fxyz(1, 2, 3))
变参函数arguments
var add = function(x) {
let sum = 0
for (const value of arguments) {
sum += value
}
return sum
}
console.log(add(1, 2, 3))
扩展运算符…
var count = function(x, ...variadic) {
return variadic.length
}
console.log(count(1, 2, 3, 4))
2. 柯里化(Currying)
柯里化是把一个多参数函数转换为一个嵌套的一元函数的过程
const add = (x, y) => x + y
const addCurried = x => y => x + y;
addCurried(3)(4)
二元curry函数
// 二元函数的柯里化
// const add = (x, y) => x + y
const curry = (binaryFn) => ((firstArg) => (secondArg) => binaryFn(firstArg, secondArg))
let autoCurriedAdd = curry((x, y) => x + y)
console.log(autoCurriedAdd(2)(3))
分析
curry(add)
返回的是一个函数(firstArg) => (secondArg) => binaryFn(firstArg, secondArg)
curry(add)(2)
返回的是一个函数(secondArg) => binaryFn(firstArg, secondArg)
curry(add)(2)(3)
返回的是binaryFn(2, 3)
柯里化用例
// 表格函数
// 假设我们想要实现这样的函数
// const tableOf2 = (y) => 2 * y
// const tableOf3 = (y) => 3 * y
// const tableOf4 = (y) => 4 * y
const genericTable = (x, y) => x * y
// genericTable(2, y) 即为 tableOf2
// genericTable(3, y) 即为 tableOf3
// genericTable(4, y) 即为 tableOf4
const tableOf2 = curry(genericTable)(2)
const tableOf3 = curry(genericTable)(3)
const tableOf4 = curry(genericTable)(4)
console.log("Tables via currying")
console.log("2 * 2 =", tableOf2(2))
console.log("2 * 3 =", tableOf2(3))
console.log("2 * 4 =", tableOf2(4))
console.log("3 * 2 =", tableOf3(2))
console.log("3 * 3 =", tableOf3(3))
console.log("3 * 4 =", tableOf3(4))
console.log("4 * 2 =", tableOf4(2))
console.log("4 * 3 =", tableOf4(3))
console.log("4 * 4 =", tableOf4(4))
多元curry函数
const multiCurry = (fn) => {
if (typeof fn !== 'function') {
throw Error("No function provided")
}
return function curriedFn(...args) {
return fn.apply(null, args)
}
}
const multiply = (x, y, z) => x * y * z
console.log(multiCurry(multiply)(1, 2, 3))
console.log(multiCurry(multiply)(1, 2, 0))
分析
-
multiCurry(multiply)
返回一个函数function curriedFn(...args) { return fn.apply(null, args) }
-
curriedFn(1, 2, 3)
返回multiply.apply(null, [1, 2, 3])
-
multiply.apply(null, [1, 2, 3])
等价于multiply(1, 2, 3)
转换为嵌套的一元函数
const curryN =(fn) => {
if(typeof fn !== 'function'){
throw Error('No function provided');
}
return function curriedFn(...args){
//make it bold
if(args.length < fn.length){
return function(){
return curriedFn.apply(null, args.concat( [].slice.call(arguments) ));
};
}
//make it bold
return fn.apply(null, args);
};
};
const multiply = (x, y, z) => x * y * z
curryN(multiply)(3)(2)(1)
3. 柯里化实战
在数组中查找数字
let match = curry(function(expr, str) {
return str.match(expr)
})
let hasNumber = match(/[0-9]+/)
let filter = curry(function(f, ary) {
return ary.filter(f)
})
let findNumbersInArray = filter(hasNumber)
findNumbersInArray(['js', 'number1'])
> [ 'number1' ]
分析
let arr = ['js', 'number1']
1. findNumbersInArray(ary) 也就是 filter(match(/[0-9]+/))(arr)
2. match(/[0-9]+/) 也就是 (str) => str.match(/[0-9]+/)
3. filter((str) => str.match(/[0-9]+/))(arr)
> "number1".match(/[0-9]+/)
[ '1', index: 6, input: 'number1', groups: undefined ]
> "number".match(/[0-9]+/)
null
> arr.filter((str) => str.match(/[0-9]+/))
[ 'number1' ]
// 检验上述的分析过程
filter((str) => str.match(/[0-9]+/))(['js', 'number2'])
[ 'number2' ]
求数组的平方
// 原始使用方式
> let arr = [1, 2, 3]
> arr.map(x => x * x)
[ 1, 4, 9 ]
// 求数组的平方
let map = curry((f, ary) => ary.map(f))
let squareAll = map(x => x * x)
let arr = [1, 2, 3]
squareAll(arr)
// 分析
squareAll(arr) => map(x => x * x)(arr) => arr.map(x => x * x)
4. 柯里化不能做的
setTimeout(() => console.log("Do X task"), 10)
setTimeout(() => console.log("Do Y task"), 10)
const setTimeoutWrapper = (time, fn) => setTimeout(fn, time)
const delayTenMs = curry(setTimeoutWrapper)(10)
delayTenMs(() => console.log("Do X task"))
delayTenMs(() => console.log("Do Y task"))
因为curry函数应用参数列表的顺序是从最左到最右,因此隐藏10需要setTimeoutWrapper
额外的开销!
5. 偏应用
实现偏应用
const partial = function (fn,...partialArgs){
let args = partialArgs.slice(0);
return function(...fullArguments) {
let arg = 0;
for (let i = 0; i < args.length && arg < fullArguments.length; i++) {
if (args[i] === undefined) {
args[i] = fullArguments[arg++];
}
}
return fn.apply(this, args);
};
};
1. setTimeout实现偏应用
delayTenMs = partial(setTimeout, undefined, 10)
delayTenMs(() => console.log("Do Y task"))
// 分析
1. partial(setTimeout, undefined, 10) 返回 function(...fullArguments) {...}
2. fullArguments = [() => console.log("Do Y task")], 因此 fullArguments.length = 1
3. args[0] = fullArguments[0]
4. args = [() => console.log("Do Y task"), 10]
5. 最后返回 setTimeout(() => console.log("Do Y task"), 10)
2. JSON.stringify实现偏应用
> let obj = {foo: 'bar', bar: 'foo'}
undefined
> JSON.stringify(obj, null, 2)
'{\n "foo": "bar",\n "bar": "foo"\n}'
let obj = {foo: 'bar', bar: 'foo'}
let prettyPrintJson = partial(JSON.stringify, undefined, null, 2)
console.log(prettyPrintJson(obj))
{
"foo": "bar",
"bar": "foo"
}
bug
let obj1 = {foo: 'bar'}
console.log(prettyPrintJson(obj1))
{
"foo": "bar",
"bar": "foo"
}
obj['bar'] = undefined
console.log(prettyPrintJson(obj))
{
"foo": "bar"
}
注意数组传递的是引用!