为什么在这个 curry 函数的实现中需要使用 bind
Why using bind is necessary in this implementation of curry function
有一个来自 https://www.30secondsofcode.org/js/s/curry 的 curry 函数。
const curry = (fn, arity = fn.length, ...args) => {
if (args.length >= arity) {
return fn(...args)
} else {
return curry.bind(null, fn, arity, ...args) // line(*)
}
}
console.log(curry(Math.pow)(2)(10)) // 1024
console.log(curry(Math.min, 3)(10)(50)(2)) // 2
为什么在第(*)行需要使用bind?可以换成
吗
return curry(fn, arity, ...args) // line(**)
?
使用 Function#bind
的原因只有两个:
- 当您想将函数绑定到特定上下文对象时。实际上,当您想提前确保函数内的
this
关键字将引用特定的已知对象时。
- 当你想预定义一些参数时,a.k.a。 “部分功能应用程序”。
第二种情况完美地解释了柯里化函数的意义——使一个 N 元函数可以灵活调用,例如:
fn(arg1, arg2, ..., argN)
,或
fn(arg1, arg2, ...)(argN)
,或
fn(arg1, arg2)(..., argN)
,或
fn(arg1)(arg2)(...)(argN)
.
这里要注意的重要一点是 我们需要多个单独的函数 用于除第一个以外的所有情况。假设您有一个带有 3 个参数的辅助函数。你可以...
- ...为 worker 函数传递足够的参数,即 3 个或更多。然后
curry()
使用这 3 个参数调用 worker 函数并 return 结果。
这使得 fn(arg1, arg2, arg3)
成为可能。
- ...为辅助函数传递的参数太少。然后
curry()
不调用 worker,但必须 return 一个新函数 接受剩余数量的参数。
这使得所有 fn(arg1)(arg2, arg3)
和 fn(arg1, arg2)(arg3)
和 fn(arg1)(arg2)(arg3)
成为可能。
Function#bind
涉及第二种情况:它为 worker 创建一个新的包装函数,并用给定的值预填充一些 worker 的参数槽。上下文对象与该意图无关,因此在这里使用 null
没问题。
const curry = (fn, arity = fn.length, ...args) =>
// enough arguments ? call : remember arguments so far & defer
args.length >= arity ? fn(...args) : curry.bind(null, fn, arity, ...args);
所以回答这个问题:不,你不能使用 return curry(fn, arity, ...args)
因为这没有做最重要的事情,即创建一个新函数。
示例: 假设我们有一个辅助函数,用于搜索字符串和 return 命中数。
const search = (str, substr) => {
var hits = 0, pos = 0;
while (true) {
pos = str.indexOf(substr, pos) + 1;
if (pos) hits++; else return hits;
}
}
现在我们可以创建一个为我们记住目标字符串的柯里化版本,我们只需要切换最后一个参数:
const flexibleSearch = curry(search);
const reusableSearch = flexibleSearch("... a ... b ... c ... a ... b ... a");
reusableSearch("a") // -> 3
reusableSearch("b") // -> 2
reusableSearch("c") // -> 1
reusableSearch("d") // -> 0
@Tomalak 是正确的
假设您有一个带有 100 个参数的函数 fn(1,2,3,...100) {}
。
fn.bind(null, 99, 100)
使用 预设前导参数 创建一个新函数。在我们的示例中,将使用参数 1,2,...97,98 的默认值,并期望两个参数作为将用作参数 99 和 100 的新函数的输入。
在这种情况下,我们柯里化一个接受部分参数的函数。
你可以通过这些来获得更深入的理解
Wiki for currying 和
MDN docs for binding partial functions
有一个来自 https://www.30secondsofcode.org/js/s/curry 的 curry 函数。
const curry = (fn, arity = fn.length, ...args) => {
if (args.length >= arity) {
return fn(...args)
} else {
return curry.bind(null, fn, arity, ...args) // line(*)
}
}
console.log(curry(Math.pow)(2)(10)) // 1024
console.log(curry(Math.min, 3)(10)(50)(2)) // 2
为什么在第(*)行需要使用bind?可以换成
吗return curry(fn, arity, ...args) // line(**)
?
使用 Function#bind
的原因只有两个:
- 当您想将函数绑定到特定上下文对象时。实际上,当您想提前确保函数内的
this
关键字将引用特定的已知对象时。 - 当你想预定义一些参数时,a.k.a。 “部分功能应用程序”。
第二种情况完美地解释了柯里化函数的意义——使一个 N 元函数可以灵活调用,例如:
fn(arg1, arg2, ..., argN)
,或fn(arg1, arg2, ...)(argN)
,或fn(arg1, arg2)(..., argN)
,或fn(arg1)(arg2)(...)(argN)
.
这里要注意的重要一点是 我们需要多个单独的函数 用于除第一个以外的所有情况。假设您有一个带有 3 个参数的辅助函数。你可以...
- ...为 worker 函数传递足够的参数,即 3 个或更多。然后
curry()
使用这 3 个参数调用 worker 函数并 return 结果。
这使得fn(arg1, arg2, arg3)
成为可能。 - ...为辅助函数传递的参数太少。然后
curry()
不调用 worker,但必须 return 一个新函数 接受剩余数量的参数。
这使得所有fn(arg1)(arg2, arg3)
和fn(arg1, arg2)(arg3)
和fn(arg1)(arg2)(arg3)
成为可能。
Function#bind
涉及第二种情况:它为 worker 创建一个新的包装函数,并用给定的值预填充一些 worker 的参数槽。上下文对象与该意图无关,因此在这里使用 null
没问题。
const curry = (fn, arity = fn.length, ...args) =>
// enough arguments ? call : remember arguments so far & defer
args.length >= arity ? fn(...args) : curry.bind(null, fn, arity, ...args);
所以回答这个问题:不,你不能使用 return curry(fn, arity, ...args)
因为这没有做最重要的事情,即创建一个新函数。
示例: 假设我们有一个辅助函数,用于搜索字符串和 return 命中数。
const search = (str, substr) => {
var hits = 0, pos = 0;
while (true) {
pos = str.indexOf(substr, pos) + 1;
if (pos) hits++; else return hits;
}
}
现在我们可以创建一个为我们记住目标字符串的柯里化版本,我们只需要切换最后一个参数:
const flexibleSearch = curry(search);
const reusableSearch = flexibleSearch("... a ... b ... c ... a ... b ... a");
reusableSearch("a") // -> 3
reusableSearch("b") // -> 2
reusableSearch("c") // -> 1
reusableSearch("d") // -> 0
@Tomalak 是正确的
假设您有一个带有 100 个参数的函数 fn(1,2,3,...100) {}
。
fn.bind(null, 99, 100)
使用 预设前导参数 创建一个新函数。在我们的示例中,将使用参数 1,2,...97,98 的默认值,并期望两个参数作为将用作参数 99 和 100 的新函数的输入。
在这种情况下,我们柯里化一个接受部分参数的函数。
你可以通过这些来获得更深入的理解 Wiki for currying 和 MDN docs for binding partial functions