在tcl中打破超过1级的foreach嵌套

时间:2015-11-11 11:33:33

标签: foreach tcl break

是否可以从两个嵌套级别退出1个命令?也就是说,让我们说我有这个代码:

foreach l { 1 2 3 4 } {
   foreach k { 3 4 5 6 } {
      if { $k > 4 } {
         break2
      } else {
         puts "$k $l"
   }
}

我希望看到的输出是:

  

1 3
  1 4

问题是,如何编码break2(如果可能的话)?。我不知道这样的"功能"在任何语言中,除了在proc中包装它,并使用return来停止proc,这比正确的语言构造更多的是hack。

2 个答案:

答案 0 :(得分:2)

有可能在Tcl≥8.5:

foreach l { 1 2 3 4 } {
    foreach k { 3 4 5 6 } {
        if {$k > 4} {
            return -code break -level 2
        } else {
            puts "$k $l"
        }
    }
}

return -code break -level 2的作用类似于“使封闭命令在堆栈的两个级别返回,就好像它已调用break”。

return command manual page

答案 1 :(得分:2)

It's not possible to do it directly; the break machinery doesn't have anything to trace up to anything other than the nearest looping context.

The easiest way of handling this is to use try in 8.6 and a custom exception code (i.e., any value from 5 upwards).

foreach a {b c} {
    puts "a=$a; to show that this is not stopping the outermost loop"
    try {
        foreach l { 1 2 3 4 } {
            foreach k { 3 4 5 6 } {
                if { $k > 4 } {
                    # Generating a custom exception code is a bit messy
                    return -level 0 -code 5
                }
                puts "$k $l"
            }
        }
    } on 5 {} {
        # Do nothing here; we've broken out
    }
}

Running that gives this output:

a=b; to show that this is not stopping the outermost loop
3 1
4 1
a=c; to show that this is not stopping the outermost loop
3 1
4 1

But it's pretty messy to do this; the best approach is typically to refactor your code so that you can just return ordinarily to end the loop. Using apply might make this easier:

foreach a {b c} {
    puts "a=$a; to show that this is not stopping the outermost loop"
    apply {{} {
        foreach l { 1 2 3 4 } {
            foreach k { 3 4 5 6 } {
                if { $k > 4 } {
                    return
                }
                puts "$k $l"
            }
        }
    }}
}

The downside of using apply is that it is a different variable context and has quite a bit more overhead (because of all the stack frame management). Still, the variable context thing can be worked around using upvar if you're careful.