Tailcall 是 Tcl 8.6 引入的新功能。功能如其名,本来是用来实现尾调用的。
proc factorial {n {accum 1}} { if {$n < 2} { return $accum } tailcall factorial [expr {$n - 1}] [expr {$accum * $n}] }
tailcall 有个实现细节:替换掉当前的栈。于是带来了一些有趣的妙用。
proc min {a b} { if {$a > $b} { tailcall max $b $a } return $a }
这个 min 函数故意绕弯,对于最坏的情况会导致两个 if 检查,但是对于一些比较复杂的函数,就让人麻烦了。比如说
# # Make sure b is greater than a! # proc ComplexCalculation {a b} { if {$a > $b} { tailcall ComplexCalculation $b $a } # Much # Code # Omitted }
尽管我们在文档里面写了要求 b 大于 a,但是有的情况下我们不知道哪个大于哪个呀!而且有的时候脑袋一懵写反了就糟糕了。在没有 tailcall 之前,我们必须得自己判断哪个大于哪个,麻烦死了,这点工作还是隐藏起来好了(从代码重用的角度看也应该这么做)。于是加上简简单单三行代码,就可以轻松省下许许多多的判断。
另外一个用处,被莫名其妙地用来实现 do ... while 控制结构……(出处,这段小小的代码也被我用到了记忆大师里面。)
proc do {code while cond} { tailcall try $code\n[list $while $cond $code] }
因为 tailcall 的一个奇妙的实现细节,我们就多了这么多乐趣!我曾经在 Tcl Chatroom 里面「抱怨」说 tailcall 不应该叫 tailcall,应该叫 replaceCurrentStackFrame。这时候有个人说,尾调用 tailcall 的意思就是在一个栈的尾部调用嘛!恍然大悟。