Как создать каскадный генератор / итератор в JavaScript?

Более новые версии 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]));

Тогда он будет отсчитывать 1000 раз от 000 до 999, что именно и делает то, что я хочу.

Недостатком является то, что вы должны использовать массивы только с 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 раз, и он не будет проходить через все 1000 чисел, как IterateCascade1 Является ли.

Кроме того, если вы передаете большие массивы, такие как

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 для запуска кода.

1 ответ

Решение

Вот решение с рекурсией

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);
}
Другие вопросы по тегам