较新版本的 JavaScript 允许将 生成器/迭代器 与
yield
关键字结合使用。
我的问题背景
考虑以下生成器,它“生成”从 0 到 9 的数字(数字):
// generator (produces numbers from 0 to 9, then stops)
function *zcounter() {
var i = 0;
while (i<=9) {
yield i;
i++;
}
}
现在我想用它来替换以下函数,该函数使用3个嵌套的
for
循环:
// e.g.: var iArray=[0,0,0];
function IterateCascade1(iArray) {
var iterations=0;
for (var x=0; x<=9; x++) {
iArray[0]=x;
for (var y=0; y<=9; y++) {
iArray[1]=y;
for (var z=0; z<=9; z++) {
iArray[2]=z;
logArray(iArray);
iterations++;
}
}
}
return iterations;
}
问题
如果您像这样调用上面的函数
console.log("Iterations: "+IterateCascade1([0,0,0]));
然后它会从000到999数1000次,这正是我想要的。
缺点是只能使用3个元素的数组,不能传递更多元素的数组。
为了使用生成器
zcounter()
解决这个问题,我尝试了以下方法:
// e.g.: var iArray=[0,0,0];
function IterateCascade2(iArray) {
var iterations=0;
// generate 3 iterators
var gArray = [];
for(var i=0; i<iArray.length; i++) {
var g=zcounter();
gArray[i]=g;
}
// loop through
for(var a in gArray) {
//console.log("a:"+a);
var g=gArray[a];
var gnext=g.next();
while (!gnext.done)
{
iArray[a]=gnext.value;
logArray(iArray);
gnext=g.next();
iterations++;
}
}
return iterations;
}
如果您像这样调用上面的函数
console.log("Iterations: "+IterateCascade2([0,0,0]));
那么它只会数 30 次,并且不会像
IterateCascade1
那样遍历所有 1000 个数字。
此外,如果您传递更大的数组,例如
console.log("Iterations: "+IterateCascade2([0,0,0,0]));
然后它会“计算”从 0 到 9 的每个数字,但不会遍历所有 10000 个组合。
问题
我知道不知何故缺少递归。
IterateCascade2
以便它做正确的事情(循环所有组合并且可以传递任何大小的整数数组)?注:
为了显示组合,我使用了
function logArray(x) {
var result="";
for(var i=0; i<x.length; i++) { result += x[i].toString(); }
console.log(result);
}
在上面的例子中。您可以使用 3 个浏览器中任意一个的开发者工具或 JSShell 来运行代码。
这是一个递归的解决方案
function iterareIndex(index, array, iterator) {
var previousValue = array[index];
for (var i = 0; i < 10; ++i) {
array[index] = i;
if (index == array.length - 1) {
iterator(array);
} else {
iterareIndex(index + 1, array, iterator);
}
}
array[index] = previousValue;
}
function IterateCascade2(array){
iterareIndex(0, array, logArray);
}
顺便说一下,这会给你相同的输出,而且可能更短:
var size = 3;
var prefix = '';
for(var i = 0; i < size; ++i) {
prefix += '0';
}
for(var i = 0, l = Math.pow(10, 3); i < l; ++i){
var printableValue = (prefix+i).slice(-size);
console.log(printableValue);
}
由于接受的答案并没有真正使用生成器,我想仍然发布另一个答案:
// generator (produces natural numbers)
function *naturals() {
for (let i = 0; true; i++) yield i;
}
// Neat way to limit an iterator; here to digits 0..9
const digits = () => naturals().take(10);
function* multiDigits(numDigits) {
if (--numDigits < 0) return yield [];
for (const digit of digits()) {
for (const rest of multiDigits(numDigits)) {
yield [digit, ...rest];
}
}
}
// Get arrays with 2 digits:
for (const result of multiDigits(2)) console.log(...result);
(请注意,代码片段的控制台面板会截断输出 - 检查您实际浏览器的控制台)