打破tcl中foreach嵌套的1个以上级别



是否可以在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,这更像是一种破解,而不是正确的语言构造
谢谢。

不可能直接执行;CCD_ 1机器除了最近的循环上下文之外没有任何东西可以追踪到。

处理此问题的最简单方法是在8.6中使用try和自定义异常代码(即,从5开始的任何值)。

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
    }
}

运行时给出以下输出:

a=b;以表明这并没有停止最外层的循环3 14 1a=c;以表明这并没有停止最外层的循环3 14 1

但这样做很麻烦;最好的方法通常是重构代码,这样您就可以正常地使用return来结束循环。使用apply可能会让这变得更容易:

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"
            }
        }
    }}
}

使用apply的缺点是它是一个不同的变量上下文,并且有相当多的开销(因为所有的堆栈帧管理)。不过,如果你小心的话,可以使用upvar来解决可变上下文的问题。

这在Tcl&ge中是可能的;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命令手册页面。

最新更新