简体   繁体   English

如何消除这种类型的递归?

[英]How to eliminate this type of recursion?

This is a bit more intricate than a simple left-recursion or tail-call recursion.这比简单的左递归或尾调用递归要复杂一些。 So I'm wondering how I can eliminate this kind of recursion.所以我想知道如何消除这种递归。 I'm already keeping my own stack as you can see below, so the function needs to no params or return values.正如您在下面看到的,我已经保留了自己的堆栈,因此 function 不需要参数或返回值。 However, it's still calling itself up (or down) to a certain level and I want to turn this into a loop, but been scratching my head over this for some time now.但是,它仍在将自己调高(或调低)到某个水平,我想把它变成一个循环,但现在我已经为此挠头了一段时间。

Here's the simplified test case, replacing all "real logic" with printf("dostuff at level #n") messages.这是简化的测试用例,用 printf("dostuff at level #n") 消息替换所有“真实逻辑”。 This is in Go but the problem is applicable to most languages.这是在 Go 但问题适用于大多数语言。 Use of loops and goto's would be perfectly acceptable (but I played with this and it gets convoluted, out-of-hand and seemingly unworkable to begin with);使用循环和 goto 是完全可以接受的(但我玩过这个并且它变得令人费解,失控并且看起来无法开始); however, additional helper functions should be avoided.但是,应避免使用额外的辅助函数。 I guess I should to turn this into some kind of simple state machine, but... which?我想我应该把它变成某种简单的 state 机器,但是……哪个? ;) ;)

As for the practicality, this is to run at about 20 million times per second (stack depth can range from 1 through 25 max later on).至于实用性,这是以每秒大约 2000 万次的速度运行(堆栈深度可以在以后从 1 到 25 max 不等)。 This is a case where maintaining my own stack is bound to be more stable / faster than the function call stack.这是维护我自己的堆栈必然比 function 调用堆栈更稳定/更快的情况。 (There are no other function calls in this function, only calculations.) Also, no garbage generated = no garbage collected. (此 function 中没有其他 function 调用,只有计算。)另外,没有产生垃圾 = 没有收集垃圾。

So here goes:所以这里是:

func testRecursion () {
    var root *TMyTreeNode = makeSomeDeepTreeStructure()
    // rl: current recursion level
    // ml: max recursion level
    var rl, ml = 0, root.MaxDepth
    // node: "the stack"
    var node = make([]*TMyTreeNode, ml + 1)

    // the recursive and the non-recursive / iterative test functions:
    var walkNodeRec, walkNodeIt func ();

    walkNodeIt = func () {
        log.Panicf("YOUR ITERATIVE / NON-RECURSIVE IDEAS HERE")
    }

    walkNodeRec = func () {
        log.Printf("ENTER LEVEL %v", rl)
        if (node[rl].Level == ml) || (node[rl].ChildNodes == nil) {
            log.Printf("EXIT LEVEL %v", rl)
            return
        }
        log.Printf("PRE-STUFF LEVEL %v", rl)
        for i := 0; i < 3; i++ {
            switch i {
            case 0:
                log.Printf("PRECASE %v.%v", rl, i)
                node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
                log.Printf("POSTCASE %v.%v", rl,  i)
            case 1:
                log.Printf("PRECASE %v.%v", rl, i)
                node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
                log.Printf("POSTCASE %v.%v", rl,  i)
            case 2:
                log.Printf("PRECASE %v.%v", rl, i)
                node[rl + 1] = node[rl].ChildNodes[rl + i]; rl++; walkNodeRec(); rl--
                log.Printf("POSTCASE %v.%v", rl,  i)
            }
        }
    }

    // test recursion for reference:
    if true {
        rl, node[0] = 0, root
        log.Printf("\n\n=========>RECURSIVE ML=%v:", ml)
        walkNodeRec()
    }

    // test non-recursion, output should be identical
    if true {
        rl, node[0] = 0, root
        log.Printf("\n\n=========>ITERATIVE ML=%v:", ml)
        walkNodeIt()
    }

} }

UPDATE -- after some discussion here, and further thinking:更新——经过这里的一些讨论,并进一步思考:

I just made up the following pseudo-code which in theory should do what I need:我刚刚编写了以下伪代码,理论上应该可以满足我的需要:

curLevel = 0
for {
    cn = nextsibling(curLevel, coords)
    lastnode[curlevel] = cn
    if cn < 8 {
        if isleaf {
            process()
        } else {
            curLevel++
        }
    } else if curLevel == 0 {
        break
    } else {
        curLevel--
    }
}

Of course the tricky part will be filling out nextsibling() for my custom use-case.当然,棘手的部分是为我的自定义用例填写 nextsibling() 。 But just as a general solution to eliminating inner recursion while maintaining the depth-first traversal order I need, this rough outline should do so in some form or another.但是,作为在保持我需要的深度优先遍历顺序的同时消除内部递归的通用解决方案,这个粗略的大纲应该以某种形式实现。

I'm not really sure I understand what it is you want to do since your recursion code looks a little strange.我不太确定我明白你想做什么,因为你的递归代码看起来有点奇怪。 However if I understand the structure of your TMyTreeNode then this is what I would do for a non recursive version.但是,如果我了解您的 TMyTreeNode 的结构,那么这就是我对非递归版本所做的。

// root is our root node
q := []*TMyTreeNode{root}
processed := make(map[*TMyTreeNode]bool
for {
  l := len(q)
  if l < 1 {
    break // our queue is empty
  }
  curr := q[l - 1]
  if !processed[curr] && len(curr.childNodes) > 0 {
    // do something with curr
    processed[curr] = true
    q = append(q, curr.childNodes...)
    continue // continue on down the tree.
  } else {
    // do something with curr
    processed[curr] = true
    q := q[:l-2] // pop current off the queue
  }
}

NOTE: This will go arbitrarily deep into the structure.注意:这将 go 任意深入到结构中。 If that's not what you want it will need some modifications.如果这不是您想要的,则需要进行一些修改。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM