我目前正在研究函数式编程技术。有关于这个问题的主题[特别是关于java],但没有关于JS的主题。
我想创建一个递归函数,它可以首先,从我指示的数字开始向上计数,直到我决定的极限,然后在达到极限时开始向下计数。我已经可以用for循环来做这件事了,但感觉像是硬编码的,因为我在循环中提供数字
基本上是这样的:
counter(0,10);
// 0, 1, 2, 3 ... 10, 9, 8... 0
以下是我的想法:
counter = (number, limit) => {
limit !=== 0
if ( number = limit ) {
counter(number -1, limit -1)
console.log(number)
} else if ( number < limit ) {
counter(number + 1, limit + 1)
}
}
这背后的想法是,如果数字低于极限计数,如果它们相等,则将每个参数递减1,以继续满足第一个if条件。
当我在v8上运行此命令时,它会给出一个rangeError";达到最大堆栈大小">
此外,这不应该是一个无限循环。
对于循环版本:
for (let i = 0; i < 11; i++ ) { console.log(i) }
for (let i = 9; i < 11 && i > -1; i--) { console.log(i) }
递归是一种函数继承,因此将其与函数样式一起使用会产生最佳结果。这意味着要避免突变、变量重新分配和其他副作用。
- 如果开始
a
大于停止b
,我们就达到了基本情况。返回空结果[]
- (电感)
a
小于或等于b
。如果a
等于b
,则返回单例结果;峰值;山的[a]
- (电感)CCD_ 9小于CCD_ 10。重复子问题
a + 1, b
,并用a
附加/预附加结果
这编码为一个纯函数表达式,下面的注释与上面的编号解释匹配-
const uppendown = (a, b) =>
a > b
? [] // #1
: a == b
? [ a ] // #2
: [ a, ...uppendown(a + 1, b), a ] // #3
const ex1 =
uppendown(0, 10)
const ex2 =
uppendown(3,7)
const ex3 =
uppendown(9,7)
console.log(JSON.stringify(ex1))
// [0,1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1,0]
console.log(JSON.stringify(ex2))
// [3,4,5,6,7,6,5,4,3]
console.log(JSON.stringify(ex3))
// []
uppenDown(3,7)
= [ 3, ...uppendDown(3 + 1, 7), 3 ] // #3
= [ 3, 4, ...uppendDown(4 + 1, 7), 4, 3 ] // #3
= [ 3, 4, 5, ...uppendDown(5 + 1, 7), 5, 4, 3 ] // #3
= [ 3, 4, 5, 6, ...uppendDown(6 + 1, 7), 6, 5, 4, 3 ] // #2
= [ 3, 4, 5, 6, 7, 6, 5, 4, 3 ]
uppendown(9,7) // #1
= []
如果出于某种任意原因,您不喜欢链接函数式三元表达式,您可以用它们交换命令式if
语句-
function uppendown (a, b)
{ if (a > b)
return [] // #1
else if (a == b)
return [ a ] // #2
else
return [ a, ...uppendown(a + 1, b), a ] // #3
}
const ex1 =
uppendown(0, 10)
const ex2 =
uppendown(3,7)
const ex3 =
uppendown(9,7)
console.log(JSON.stringify(ex1))
// [0,1,2,3,4,5,6,7,8,9,10,9,8,7,6,5,4,3,2,1,0]
console.log(JSON.stringify(ex2))
// [3,4,5,6,7,6,5,4,3]
console.log(JSON.stringify(ex3))
// []
如果你想让数字一个接一个地出来,而不是返回一个数组,你可以使用JavaScript的生成器。注意每个程序变体之间惊人的相似性-
function* uppendown (a, b)
{ if (a > b)
return // #1
else if (a == b)
yield a // #2
else
( yield a // #3
, yield *uppendown(a + 1, b) //
, yield a //
)
}
for (const x of uppendown(3, 7))
console.log(x)
// 3
// 4
// 5
// 6
// 7
// 6
// 5
// 4
// 3
您不需要向下循环或递减值,因为当您达到基本情况(停止递归的东西)时,您将跳回调用函数,该函数保存以前的value
:
counter(0, 10) // logs: 0
| ^
| | (returns back to)
|---> counter(1, 10) // logs: 1
| ^
| | (returns back to)
|---> counter(2, 10) // logs: 2 <---
| | (returns back to)
| |
........ ---> counter(10, 10) // logs: 10 - base case
counter()
的每次调用都将再次调用计数器(如上图所示,带有子counter
调用),然后这些调用将打印其当前的value
。当您到达基本情况时,您将打印当前值并返回,这将使您将控制权传递回调用函数。我的意思是,当你调用一个函数时,这个函数就会运行。当函数结束时,代码会从函数最初调用的位置返回:
function bar() {
console.log("bar");
}
console.log("foo"):
bar(); // call the function makes the code execution jump up into `bar` function. When that completes, our code execution jumps back to the next line, which logs "baz"
console.log("baz");
在我们的counter()
示例中,调用子counter()
函数的地方是它的父counter
函数,当子函数完成执行(返回)时,我们将跳转(将控制传递回)它。一旦控制权被传递回来。对于调用函数(即上图中的父函数),您可以再次记录的value
,因为它包含value
:的前一个值
function counter(value, limit) {
if(value === limit) {
console.log(value);
} else {
console.log(value); // on the way down / going deeper (increment)
counter(value+1, limit);
console.log(value); // on the way up / coming up from the depths (decrement)
}
}
counter(0,10);
// 0, 1, 2, 3 ... 10, 9, 8... 0
类似的东西?
console.clear();
{
"use strict"
// Curry function
const nextStepInit = start => max => {
let increment = 1;
let counter = start;
return () => {
counter += increment;
if (counter >= max) {
increment = -increment;
return max;
}
if (counter <= start) {
return start;
}
return counter;
}
}
// this is a function, because nextStep(start)(max) returns a function
const nextStep = nextStepInit(0)(10)
const output = result => document.getElementById('output').value = result
const onClick = () => output(nextStep())
output(0)
document.getElementById('next-step').addEventListener('click', onClick)
}
<button id="next-step">Next Step</button><br>
<input id="output" disabled/>