Tclにはgotoみたいに直列に書いたコードをスキップする制御構文がない。
例えばなんかデータを受信して、チェックしたり加工したりしたあとでどこかに記録するけど、
結果によらず受信回数はカウントしときたい場合、こんなコードを書いたとする。
proc on_receive {data} { global cnt set res [validate $data] if {$res == 1} { incr cnt; return $res } set res [convert $data data2] if {$res == 2} { incr cnt; return $res } set res [save $data2] incr cnt; return $res } |
incr cnt; return $res; というコードを何度も書かないといけないのが嫌だ。
そこで今まではこんな書き方をしてたことがあった。
proc on_receive {data} { global cnt while {1} { set res [validate $data] if {$res == 1} break set res [convert $data data2] if {$res == 2} break set res [save $data2] break; # Don't forget! } incr cnt; return $res } |
ときどきだけど、while {1} の最後のbreakを忘れて、
無限ループに陥ることがあったので、ケアレスミスを減らすため、こう変えた。
proc on_receive {data} { global cnt foreach once {1} { set res [validate $data] if {$res == 1} break set res [convert $data data2] if {$res == 2} break set res [save $data2] } incr cnt; return $res } |
ちなみにforeachのところは、こうしても同じ。
proc on_receive {data} { global cnt for {} {1} break { # ... } |
だけど何とも見た目が悪い。
初めて見た人は foreach {var1 var2} {1 2} break 並みに分かりづらいだろう。
多重代入のためにlassignが導入されたように、やはり目的が分かる名前の制御構文があったほうがかっこいい。
8.6のtry(8.5ではtcllibに入ってる)を使って、blockという途中でbreak可能な制御構文を作ってみる。
if {[info tclversion] <= 8.5} { package require try } proc block {script} { try { return [uplevel 1 $script] } on break {result options} {} } |
さっきのコードを書き換えてみる。
proc on_receive {data} { global cnt block { set res [validate $data] if {$res == 1} break set res [convert $data data2] if {$res == 2} break set res [save $data2] } incr cnt return $res } |
どうかな。
これだけでblockなんて名乗るのはおこがましいので、もうちょい慎ましい名前を考えたら使ってみようと思う。
候補
- breakable
- through
Tclだとそんなところに脳のリソースを割かないといけないの?とか言わないでね。