Newer versions of JavaScript allow to use generators/iterators in combination with the yield
keyword.
Background of my question
Consider the following generator, which "produces" numbers (digits) from 0 to 9:
// generator (produces numbers from 0 to 9, then stops)
function *zcounter() {
var i = 0;
while (i<=9) {
yield i;
i++;
}
}
Now I want to use it to replace the following function, which uses 3 nested for
loops:
// 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;
}
The problem
If you invoke the function above like
console.log("Iterations: "+IterateCascade1([0,0,0]));
Then it will count 1000 times from 000 to 999, which is exactly doing what I want.
The disadvantage is, that can only use arrays with 3 elements, you can't pass arrays with more elements.
To solve it by using the generator zcounter()
, I tried the following:
// 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;
}
If you invoke the function above like
console.log("Iterations: "+IterateCascade2([0,0,0]));
Then it will count only 30 times, and it will not go through all 1000 numbers as IterateCascade1
does it.
Furthermore, if you pass larger arrays like
console.log("Iterations: "+IterateCascade2([0,0,0,0]));
then it will "count" every digit from 0 to 9, but will not go through all 10000 combinations.
Question
I know that somehow there is a recursion missing.
IterateCascade2
be modified so it does the right thing (cycle through all combinations and you can pass integer arrays of any size)?Note:
To display the combinations, I have used
function logArray(x) {
var result="";
for(var i=0; i<x.length; i++) { result += x[i].toString(); }
console.log(result);
}
in the examples above. You can use the developer tools of any of the 3 browsers or JSShell to run the code.
Here is how you can use a given generator -- called digits()
here -- to get combined results with a new, recursive generator -- called multiDigits()
:
// 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);
(Note that the snippet's console panel truncates the output -- check your actual browser's console)