Рекурсивные функции в Javascript и отслеживание глубины

Я пишу рекурсивную функцию в JS и возникли некоторые проблемы. Давайте начнем с этой самой основной функции:

function traverse(thing)
{
    if (typeof traverse.depth == 'undefined')
        traverse.depth = 1;
    else
        traverse.depth ++;

    if (thing.child)
        traverse(thing.child);
}

Так что это работает отлично, и depth действует как статический var, но проблема в том, что в таком языке, как C, который имеет правильные статические vars, когда вы выходите из функции, эта переменная будет (якобы) уменьшаться, поэтому это истинная глубина. Если у меня есть три коробки, которые содержат три коробки, и каждая из них содержит три коробки и т. д. мы по сути углубляясь в самую глубокую, пока не останется детей, затем отступая на уровень к брату и сестре, и пересекая его детей. Проблема с вышеуказанным кодом заключается в том, что глубина продолжает увеличиваться и увеличиваться бесконечно, даже если глубина истины может быть только 3 или 4 от самого старшего предка до самого младшего ребенка. Если на каждом уровне есть 80 братьев и сестер, счетчик глубины взлетит до небес.

Как отслеживать истинную глубину в рекурсивных функциях JS?

5 ответов


не присоединяйте счетчик к функции. Счетчик является общим для всех рекурсивных вызовов, поэтому счетчик представляет количество вызовов функций вместо глубины рекурсии.

, когда depth передается как отдельная переменная, счетчик показывает истинную глубину.

function traverse(thing, depth)
{
    if (typeof depth == 'number')
        depth++;
    else
        depth = 1;

    if (thing.child)
        traverse(thing, depth);
}

другим (и, возможно, более приятным) решением было бы использовать сильные стороны функционального программирования JS и использовать функцию высокого порядка, чтобы сохранить все связанные с глубиной домохозяйства вне основной функции. Рассмотрим, например, следующий классический пример:

function fac(n) {
    if(n < 3)
        return n;
    return n * fac(n - 1);
}

мы хотим, чтобы этот сломал рекурсию, как только она будет глубже, чем заданное значение. Давайте закодируем обертку:

function wrapDepth(fn, max) {
    var depth = 0
    return function() {
        if (++depth > max) 
            throw "Too much recursion"
        var out = fn.apply(null, [].slice.call(arguments, 0))
        depth--;
        return out;
    }
}

создайте обертку с максимальной глубиной = 20:

fac = wrapDepth(fac, 20)

и тест:

 console.log(fac(10)) // 3628800
 console.log(fac(100)) // Too much recursion

обратите внимание, что мы не делали никаких изменений в функции main fac сам, но все же его глубина рекурсии теперь находится под контролем.


почему бы тебе просто не изменить сигнатуру функции, чтобы взять вещь и индекс? Поэтому вы бы назвали это так:

function traverse(thing, idx)
    ...
    if (condition)
        traverse(thing.child, ++idx)

просто добавить:

traverse.depth--;

прямо перед любыми операторами возврата. Так что

if(x === 5)
    return thing;

станет:

if(x === 5){
    traverse.depth--;
    return thing;
}

и затем добавить traverse.depth--; перед закрывающим } функции.


Если я правильно понял, мне кажется, что вы хотите отслеживать глубину рекурсии.. но в вашем коде вы никогда не уменьшаете глубину по мере завершения 1 уровня в рекурсии.

Я пробовал простой код, и я думаю, что это то, что вы хотели,

демо

HTML-код:

<div id="result"></div>

JS:

var result = document.getElementById('result');

var tmpArray = [1,2,3,4,5,6,7,8];

function depthTest() {
    if (typeof depthTest.depth == 'undefined')
        depthTest.depth = 0;
    else
        depthTest.depth++;

    result.innerHTML += depthTest.depth;

    if (typeof tmpArray[depthTest.depth] != 'undefined')
        depthTest();

    result.innerHTML += depthTest.depth;
    depthTest.depth--;
}

depthTest(tmpArray);

выход:

012345678876543210