将递归函数转换为while循环

时间:2019-09-15 16:50:32

标签: javascript

我的递归函数存在潜在的堆栈溢出问题。通常,我可以使用while循环和一个条件来解决此问题,但是我无法弄清楚基于while循环的条件。

这是当前的递归函数,该函数计算未知嵌套对象数的对象中处理程序的数量。

countHandlers(obj){
    let count = 0;
    for(let k in obj){
        if(k === "_handlers"){
            count += obj[k].length;
        }
        else if(typeof obj[k] === 'object') {
            count += this.countHandlers(obj[k])
        }
    }
    return count;
}

可以将其转换为非递归函数吗?

2 个答案:

答案 0 :(得分:4)

我通常绕过递归函数的方式是使用堆栈或队列来维护需要处理的数据。 在JavaScript中使用堆栈更容易,因此我们将继续介绍。 :)

function countHandlers(obj) {
    let stack = [];
    stack.push(obj);

    let count = 0;
    while (stack.length > 0) {
        let currentObj = stack.pop();   

        for (let k in currentObj) {
            if (k === "_handlers") {
                count += currentObj[k].length;
            }
            else if (typeof currentObj[k] === 'object') {
                stack.push(currentObj[k]);
            }
        }
    }

    return count;
}

答案 1 :(得分:-1)

当您具有循环引用时,在此类递归函数中会出现问题。您必须跟踪已解析的对象。

假设我们有这个对象:

var test = {
    _handlers: {
        length: 1
    }, 
    child1: {
        member1: {
            _handlers: [7, 9, 12], 
            child: {
                morehandlers: {
                    _handlers: {
                        length: 7
                    }
                }, 
                _handlers: [1]
            }
        }, 
        member2: {
            _handlers: {
                length: 1
            }
        }
    }, 
    child2: {
        value: 2
    }, 
    child3: {
        last: {
            _handlers: {
                length: 7
            }
        }
    }
}

处理程序总数应为20。

然后我们添加一个循环引用:

test.child1.member3 = test;

这是我不考虑表演的情况下处理它的方法:

let parsedHandlers = null;
let handlersCountLaunched = false;
function countHandlers(obj) { // Cannot be async
    let countObj = obj;
    let count = 0;
    for (let i = 0; i < parsedHandlers.length; i++) {
        if (countObj === parsedHandlers[i]) {
            countObj = null;
            break;
        }
    }
    if (countObj !== null) {
        parsedHandlers.push(countObj);
        for (let k in obj) {
            if (k === "_handlers") {
                count += obj[k].length;
            } else if (typeof obj[k] === 'object') {
                count += this.countHandlers(obj[k]);
            }
        }
    }
    return count;
}
function getHandlersCount(mainObj) {
    if (!handlersCountLaunched) {
        parsedHandlers = [];
        handlersCountLaunched = true;
        let count = countHandlers(mainObj);
        handlersCountLaunched = false;
        parsedHandlers = null;
        return count;
    } else {
        console.error('TimingError : getHandlersCount() has been called and has not yet finished counting');
        return -1;
    }
}

console.log(getHandlersCount(test));

在javascript中,除非您设置了映射逻辑,否则您将无法检索成员的父对象。在对象中使用循环引用,除非选择没有循环引用的分支,否则您可能最终会在对象树中得到处理程序总数。