在 JavaScript 中调用和传递嵌套函数
Calling and Passing Nested Functions in JavaScript
我有一个函数可以 returns 一系列数字的 LCM。它工作得很好,但是它在函数内部的函数内部有一个函数。我的问题是为什么我不能通过从内部删除 scm() 来简化嵌套的 smallestCommon() ?为什么这个特定的解决方案需要这个 if else 嵌套如此深的功能?
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
var candidate = max;
var smallestCommon = function(low, high) {
// inner function to use 'high' variable
function scm(l, h) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + high);
}
}
return scm(low, high);
};
for (var i = min; i <= max; i += 1) {
candidate = smallestCommon(i, candidate);
}
return candidate;
}
smallestCommons([5, 1]); // should return 60
smallestCommons([1, 13]); // should return 360360
smallestCommons([23, 18]); //should return 6056820
如果您以这样的方式重写内部函数,使其不在其外部作用域中引用变量,您可以解除嵌套。
function scm(l, h, step) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + h, step);
}
}
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
return scm(min, max, max);
}
虽然它可能会耗尽你的筹码,但那是另一个问题。如果得到 RangeError
,则必须重写 scm
以基于循环而不是递归。
拥有内部函数并不一定是坏事。有时你想减少一些局部重复,但又不想创建一个新的顶级函数。小心使用,它们可以清理代码。
但在您的特定情况下,没有必要嵌套它。你可以只传入 high
变量作为第三个参数:
function scm(l, h, high) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + high, high);
}
}
function smallestCommon (low, high) {
return scm(low, high, high);
}
这实际上是处理递归时相当常见的模式:有一个递归函数,以及一个简化递归函数调用的辅助函数。在递归很常见的函数式语言中,it's actually commonplace to have a local recursive function like you had originally (often called something like go
).
很遗憾 JS doesn't have a range
function。 smallestCommons
基本上只是 [min,max]
范围内的缩减。在缺少 range
函数和 smallestCommon
参数顺序错误之间,不幸的是,将您的代码转换为使用 reduce
有点笨重:
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
return Array.from(new Array(max - min), (x,i) => i + min)
.reduce((acc, i) => smallestCommon(i, acc), max);
}
我建议将其分解成更小的部分。您将拥有许多易于编写和调试的函数,而不是一个复杂且难以调试的函数。较小的函数也更容易在程序的其他部分进行测试和重用 -
const gcd = (m, n) =>
n === 0
? m
: gcd (n, m % n)
const lcm = (m, n) =>
Math.abs (m * n) / gcd (m, n)
console.log
( lcm (1, 5) // 5
, lcm (3, 4) // 12
, lcm (23, 18) // 414
)
现在我们有 minmax
。此实现的独特之处在于它仅使用 单次 遍历输入数组 -
来找到最小值和最大值
const None =
Symbol ()
const list = (...values) =>
values
const minmax = ([ x = None, ...rest ], then = list) =>
x === None
? then (Infinity, -Infinity)
: minmax
( rest
, (min, max) =>
then
( Math.min (min, x)
, Math.max (max, x)
)
)
console.log
( minmax ([ 3, 4, 2, 5, 1 ]) // [ 1, 5 ]
, minmax ([ 1, 5 ]) // [ 1, 5 ]
, minmax ([ 5, 1 ]) // [ 1, 5 ]
, minmax ([ 9 ]) // [ 9, 9 ]
, minmax ([]) // [ Infinity, -Infinity ]
)
默认情况下 minmax
returns 最小值和最大值的 list
。我们可以将 min 和 max 直接插入到一个 range
函数中,这可能对我们更有用,我们稍后会看到 -
const range = (m, n) =>
m > n
? []
: [ m, ... range (m + 1, n ) ]
console.log
( minmax ([ 3, 4, 2, 5, 1 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 1, 5 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 5, 1 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 9 ], range) // [ 9 ]
, minmax ([], range) // []
)
现在我们可以找到输入的最小值和最大值,在两者之间创建一个范围,剩下的就是计算范围内值的 lcm
。使用 .reduce -
获取多个值并将它们减少为单个值
console.log
( minmax ([1, 5], range) .reduce (lcm, 1) // 60
, minmax ([5, 1], range) .reduce (lcm, 1) // 60
)
将其封装在一个函数中,我们就完成了 -
const smallestCommons = xs =>
minmax (xs, range) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
下面在自己的浏览器中验证结果-
const gcd = (m, n) =>
n === 0
? m
: gcd (n, m % n)
const lcm = (m, n) =>
Math.abs (m * n) / gcd (m, n)
const None =
Symbol ()
const list = (...values) =>
values
const minmax = ([ x = None, ...xs ], then = list) =>
x === None
? then (Infinity, -Infinity)
: minmax
( xs
, (min, max) =>
then
( Math.min (min, x)
, Math.max (max, x)
)
)
const range = (m, n) =>
m > n
? []
: [ m, ... range (m + 1, n ) ]
const smallestCommons = xs =>
minmax (xs, range) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
额外
上面,minmax
是使用continuation passing style定义的。我们通过传递 range
作为指定的延续 (then
) 来节省额外的计算。但是,我们可以调用 minmax
而无需指定延续并将中间值展开 (...
) 到 range
。任何一个程序都可能对您更有意义。结果是一样的-
const smallestCommons = xs =>
range (...minmax (xs)) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
同一头猪,不同的农场
smallestCommons
is basically just a reduction over the range [min,max]
-
希望它有助于从多种方法中看到相同的结果:D
来源
有些人会鄙视minmax
的上述实现,不管它的优雅和灵活。现在我们可能更好地理解了减少,我们可以展示如何使用直接样式更好地实现 minmax
-
const minmax = xs =>
xs .reduce
( ([ min, max ], x) =>
[ Math.min (min, x)
, Math.max (max, x)
]
, [ Infinity, -Infinity ]
)
const smallestCommons = xs =>
range (...minmax (xs)) .reduce (lcm, 1) // direct style now required here
我有一个函数可以 returns 一系列数字的 LCM。它工作得很好,但是它在函数内部的函数内部有一个函数。我的问题是为什么我不能通过从内部删除 scm() 来简化嵌套的 smallestCommon() ?为什么这个特定的解决方案需要这个 if else 嵌套如此深的功能?
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
var candidate = max;
var smallestCommon = function(low, high) {
// inner function to use 'high' variable
function scm(l, h) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + high);
}
}
return scm(low, high);
};
for (var i = min; i <= max; i += 1) {
candidate = smallestCommon(i, candidate);
}
return candidate;
}
smallestCommons([5, 1]); // should return 60
smallestCommons([1, 13]); // should return 360360
smallestCommons([23, 18]); //should return 6056820
如果您以这样的方式重写内部函数,使其不在其外部作用域中引用变量,您可以解除嵌套。
function scm(l, h, step) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + h, step);
}
}
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
return scm(min, max, max);
}
虽然它可能会耗尽你的筹码,但那是另一个问题。如果得到 RangeError
,则必须重写 scm
以基于循环而不是递归。
拥有内部函数并不一定是坏事。有时你想减少一些局部重复,但又不想创建一个新的顶级函数。小心使用,它们可以清理代码。
但在您的特定情况下,没有必要嵌套它。你可以只传入 high
变量作为第三个参数:
function scm(l, h, high) {
if (h % l === 0) {
return h;
} else {
return scm(l, h + high, high);
}
}
function smallestCommon (low, high) {
return scm(low, high, high);
}
这实际上是处理递归时相当常见的模式:有一个递归函数,以及一个简化递归函数调用的辅助函数。在递归很常见的函数式语言中,it's actually commonplace to have a local recursive function like you had originally (often called something like go
).
很遗憾 JS doesn't have a range
function。 smallestCommons
基本上只是 [min,max]
范围内的缩减。在缺少 range
函数和 smallestCommon
参数顺序错误之间,不幸的是,将您的代码转换为使用 reduce
有点笨重:
function smallestCommons(arr) {
var max = Math.max(...arr);
var min = Math.min(...arr);
return Array.from(new Array(max - min), (x,i) => i + min)
.reduce((acc, i) => smallestCommon(i, acc), max);
}
我建议将其分解成更小的部分。您将拥有许多易于编写和调试的函数,而不是一个复杂且难以调试的函数。较小的函数也更容易在程序的其他部分进行测试和重用 -
const gcd = (m, n) =>
n === 0
? m
: gcd (n, m % n)
const lcm = (m, n) =>
Math.abs (m * n) / gcd (m, n)
console.log
( lcm (1, 5) // 5
, lcm (3, 4) // 12
, lcm (23, 18) // 414
)
现在我们有 minmax
。此实现的独特之处在于它仅使用 单次 遍历输入数组 -
const None =
Symbol ()
const list = (...values) =>
values
const minmax = ([ x = None, ...rest ], then = list) =>
x === None
? then (Infinity, -Infinity)
: minmax
( rest
, (min, max) =>
then
( Math.min (min, x)
, Math.max (max, x)
)
)
console.log
( minmax ([ 3, 4, 2, 5, 1 ]) // [ 1, 5 ]
, minmax ([ 1, 5 ]) // [ 1, 5 ]
, minmax ([ 5, 1 ]) // [ 1, 5 ]
, minmax ([ 9 ]) // [ 9, 9 ]
, minmax ([]) // [ Infinity, -Infinity ]
)
默认情况下 minmax
returns 最小值和最大值的 list
。我们可以将 min 和 max 直接插入到一个 range
函数中,这可能对我们更有用,我们稍后会看到 -
const range = (m, n) =>
m > n
? []
: [ m, ... range (m + 1, n ) ]
console.log
( minmax ([ 3, 4, 2, 5, 1 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 1, 5 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 5, 1 ], range) // [ 1, 2, 3, 4, 5 ]
, minmax ([ 9 ], range) // [ 9 ]
, minmax ([], range) // []
)
现在我们可以找到输入的最小值和最大值,在两者之间创建一个范围,剩下的就是计算范围内值的 lcm
。使用 .reduce -
console.log
( minmax ([1, 5], range) .reduce (lcm, 1) // 60
, minmax ([5, 1], range) .reduce (lcm, 1) // 60
)
将其封装在一个函数中,我们就完成了 -
const smallestCommons = xs =>
minmax (xs, range) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
下面在自己的浏览器中验证结果-
const gcd = (m, n) =>
n === 0
? m
: gcd (n, m % n)
const lcm = (m, n) =>
Math.abs (m * n) / gcd (m, n)
const None =
Symbol ()
const list = (...values) =>
values
const minmax = ([ x = None, ...xs ], then = list) =>
x === None
? then (Infinity, -Infinity)
: minmax
( xs
, (min, max) =>
then
( Math.min (min, x)
, Math.max (max, x)
)
)
const range = (m, n) =>
m > n
? []
: [ m, ... range (m + 1, n ) ]
const smallestCommons = xs =>
minmax (xs, range) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
额外
上面,minmax
是使用continuation passing style定义的。我们通过传递 range
作为指定的延续 (then
) 来节省额外的计算。但是,我们可以调用 minmax
而无需指定延续并将中间值展开 (...
) 到 range
。任何一个程序都可能对您更有意义。结果是一样的-
const smallestCommons = xs =>
range (...minmax (xs)) .reduce (lcm, 1)
console.log
( smallestCommons ([ 5, 1 ]) // 60
, smallestCommons ([ 1, 13 ]) // 360360
, smallestCommons ([ 23, 18 ]) // 6056820
)
同一头猪,不同的农场
smallestCommons
is basically just a reduction over the range[min,max]
-
希望它有助于从多种方法中看到相同的结果:D
来源
有些人会鄙视minmax
的上述实现,不管它的优雅和灵活。现在我们可能更好地理解了减少,我们可以展示如何使用直接样式更好地实现 minmax
-
const minmax = xs =>
xs .reduce
( ([ min, max ], x) =>
[ Math.min (min, x)
, Math.max (max, x)
]
, [ Infinity, -Infinity ]
)
const smallestCommons = xs =>
range (...minmax (xs)) .reduce (lcm, 1) // direct style now required here