我想在递归中转换这个嵌套循环。我如何实现这一目标?
for(let i = 0; i < 5; i++) {
for(let j = 0; j < 5; j++) {
console.log(i,j);
}
}
答案 0 :(得分:3)
这是递归的另一个例子:
function loop(i,j,limitI,limitJ){
if(i>=limitI) return;
if(j>=limitJ) loop(i+1,0,limitI,limitJ);
else{
console.log(i,j);
loop(i,j+1,limitI,limitJ)
}
}
loop(0,0,4,4);
答案 1 :(得分:3)
通用函数product
计算其输入的Cartesian product - 如果它不在您的环境中,您可以填充Array.prototype.flatMap
Array.prototype.flatMap = function (f, context)
{
return this.reduce ((acc, x) => acc.concat (f (x)), [])
}
const product = (first = [], ...rest) =>
{
const loop = (comb, first, ...rest) =>
rest.length === 0
? first.map (x => [ ...comb, x ])
: first.flatMap (x => loop ([ ...comb, x ], ...rest))
return loop ([], first, ...rest)
}
const suits =
['♤', '♡', '♧', '♢']
const ranks =
['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
for (const card of product (ranks, suits))
console.log (card)
// [ 'A', '♤' ]
// [ 'A', '♡' ]
// [ 'A', '♧' ]
// [ 'A', '♢' ]
// [ '2', '♤' ]
// ...
// [ 'Q', '♧' ]
// [ 'K', '♤' ]
// [ 'K', '♡' ]
// [ 'K', '♧' ]
// [ 'K', '♢' ]
product
是一个可变函数(使用rest parameter),它接受一个或多个输入
const range = (min = 0, max = 0) =>
max < min
? []
: [ min, ...range (min + 1, max) ]
const r =
range (0, 2)
for (const comb of product (r, r, r))
console.log (comb)
// [ 0, 0, 0 ]
// [ 0, 0, 1 ]
// [ 0, 0, 2 ]
// [ 0, 1, 0 ]
// ...
// [ 2, 1, 2 ]
// [ 2, 2, 0 ]
// [ 2, 2, 1 ]
// [ 2, 2, 2 ]
使用destructuring assignment,您可以有效地创建嵌套循环
for (const [ i, j ] of product (range (0, 5), range (0, 5)))
console.log ("i %d, j %d", i, j)
// i 0, j 0
// i 0, j 1
// i 0, j 2
// i 0, j 3
// i 0, j 4
// i 0, j 5
// i 1, j 0
// ...
// i 4, j 5
// i 5, j 0
// i 5, j 1
// i 5, j 2
// i 5, j 3
// i 5, j 4
// i 5, j 5
product
也可以使用generators撰写 - 在下面,我们发现所有完美的Pythagorean triples都不到20
const product = function* (first, ...rest)
{
const loop = function* (comb, first, ...rest)
{
if (rest.length === 0)
for (const x of first)
yield [ ...comb, x ]
else
for (const x of first)
yield* loop ([ ...comb, x ], ...rest)
}
yield* loop ([], first, ...rest)
}
const range = (min = 0, max = 0) =>
max < min
? []
: [ min, ...range (min + 1, max) ]
const pythagTriple = (x, y, z) =>
(x * x) + (y * y) === (z * z)
const solver = function* (max = 20)
{
const N = range (1, max)
for (const [ x, y, z ] of product (N, N, N))
if (pythagTriple (x, y, z))
yield [ x, y, z ]
}
console.log ('solutions:', Array.from (solver (20)))
// solutions:
// [ [ 3, 4, 5 ]
// , [ 4, 3, 5 ]
// , [ 5, 12, 13 ]
// , [ 6, 8, 10 ]
// , [ 8, 6, 10 ]
// , [ 8, 15, 17 ]
// , [ 9, 12, 15 ]
// , [ 12, 5, 13 ]
// , [ 12, 9, 15 ]
// , [ 12, 16, 20 ]
// , [ 15, 8, 17 ]
// , [ 16, 12, 20 ]
// ]
我认为使用
map
(和reduce
),虽然它允许你演示的更复杂的递归结构,但实际上是一个隐式的for
循环,它并没有真正回答这个问题关于如何将一个转换为重复。但是,如果你还定义了一个递归map
和reduce
,那就没问题了:) - גלעדברקן
你的愿望是我的命令:D
const Empty =
Symbol ()
const concat = (xs, ys) =>
xs.concat (ys)
const append = (xs, x) =>
concat (xs, [ x ])
const reduce = (f, acc = null, [ x = Empty, ...xs ]) =>
x === Empty
? acc
: reduce (f, f (acc, x), xs)
const mapReduce = (m, r) =>
(acc, x) => r (acc, m (x))
const map = (f, xs = []) =>
reduce (mapReduce (f, append), [], xs)
const flatMap = (f, xs = []) =>
reduce (mapReduce (f, concat), [], xs)
const product = (first = [], ...rest) =>
{
const loop = (comb, first, ...rest) =>
rest.length === 0
? map (x => append (comb, x), first)
: flatMap (x => loop (append (comb, x), ...rest), first)
return loop ([], first, ...rest)
}
const suits =
['♤', '♡', '♧', '♢']
const ranks =
['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K']
for (const card of product (ranks, suits))
console.log (card)
// same output as above
答案 2 :(得分:1)
这是另一种选择。
这种方法使用逗号运算符进行参数初始化(只是为了缩短代码)。
此外,运算符param(回调)为每次迭代执行任何逻辑。
function loop(n, operator, i = 0, j = 0) { // Param initialization.
if (j === n) (j = 0, i++); // Comma operator.
if (i === n) return;
operator(i, j);
loop(n, operator, i, ++j);
}
loop(5, (i, j) => console.log(i, j));
.as-console-wrapper { max-height: 100% !important; top: 0; }
答案 3 :(得分:1)
您可以通过获取深度和值来迭代:
function loop(start, end, depth, exit, ...args){
for(let i = start; i < end; i++)
depth ? loop(start, end, depth - 1, exit, ...args, i) : exit(...args, i);
}
可用作:
loop(0, 5, 1, (i, j) => console.log(i, j))
唯一真正的用例是更深的循环,例如this one
如果你想完全没有:
const range = (start, end, cb) =>
(cb(start), start + 1 >= end || range (start + 1, end, cb));
function loop(start, end, depth, exit, ...args){
range(start, end, i =>
depth ? loop(start, end, depth - 1, exit, ...args, i) : exit(...args, i));
}
答案 4 :(得分:1)
我不建议这样做,但你可以这样做(因为它很难阅读,为了便于阅读和理解你的代码是最好的。)
function forLoop(i,j){
if(j===0){
if(i!==0)
forLoop(i-1,4);
console.log(i,j);
}
else{
forLoop(i,j-1);
console.log(i,j);
}
}
forLoop(4,4);
答案 5 :(得分:1)
这是我的演绎:
function nested(i, j, maxI, maxJ) {
if (i == maxI) return
console.log(i, j)
if (i < maxI) {
++j < maxJ ? nested(i, j, maxI, maxJ) : nested(++i, 0, maxI, maxJ)
}
}
nested(0, 0, 5, 5)
&#13;
答案 6 :(得分:1)
您可以将数组用于限制和值。由于首先递增最低指数,顺序相反。
这适用于任意数量的嵌套循环,并允许使用最大值的任意限制。
function iter(limit, values = limit.map(_ => 0)) {
console.log(values.join(' '));
values = values.reduce((r, v, i) => {
r[i] = (r[i] || 0) + v;
if (r[i] >= limit[i]) {
r[i] = 0;
r[i + 1] = (r[i + 1] || 0) + 1;
}
return r;
}, [1]);
if (values.length > limit.length) {
return;
}
iter(limit, values);
}
iter([2, 3]);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
答案 7 :(得分:1)
这是&#34; recurrence relation,&#34;的概述。其中&#34;序列的每个进一步的术语......被定义为前面术语的函数。&#34;
您可能已经知道,递归函数通常至少有一个基本情况,终止递归,以及至少一个递归调用。要查找模式,请检查序列:
0,0
我们的基本情况,即对前一个参数的调用终止,似乎是function f(i, j){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
}
。但这也是控制台日志开始的地方,这意味着我们首先必须一直调用回基本情况。为方便起见,我们假设函数需要正参数:
i
我们还可以注意到外部循环j
在function f(i, j){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
if (j == 0)
// ... what happens here?
}
s的每个循环中保持不变:
j
但是我们陷入了困境。当f(i, j - 1)
大于零时,我们可以确定当前字词来自j
,但如果function f(i, j, jj){
if (i == 0 && j == 0){
console.log(i,j);
return;
}
if (j == 0)
f(i - 1, jj, jj);
else
f(i, j - 1, jj);
console.log(i,j);
}
f(4,4,4);
在当前字词中为零,则我们无法确定其中的内容前一任期。我们还需要一个参数:
Set.containsAll(Collection<?> c);
&#13;
答案 8 :(得分:1)
将嵌套的for循环转换为它的递归副本是非常困难的。好问题!
您可以将每个循环(没有堆栈)转换为尾递归算法。所以这个规则也应该适用于嵌套循环。
我认为我们需要两个不同的函数来获得与两个嵌套循环等效的东西:
const loop = ([i, j], [k, l]) => {
const loop_ = (k_, l_) => {
if (k_ >= l_) return;
else {
console.log(i, k_);
loop_(k_ + 1, l_);
}
};
if (i >= j) return;
else {
loop_(k, l);
loop([i + 1, j], [k, l]);
}
};
loop([0, 5], [0, 5]);
&#13;
你必须传递out和inner循环的范围。
正如您所看到的,两个递归调用都处于尾部位置。我认为这是我们能得到的最接近的等价物。
答案 9 :(得分:0)
建议的解决方案
function recurse(arg1=0, arg2=0, cb) {
if ( arg2 <= 5 ) {
let _l = arg2++;
if ( arg1 === 5 )
return ;
if ( ++_l === 6 ) {
arg2 = 0;
cb(arg1++, arg2);
recurse(arg1, arg2, cb);
} else {
cb(arg1, arg2 - 1);
recurse(arg1, arg2, cb);
}
}
}
recurse( 0 , 0 , (i,j) => console.log(i,j));