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
} |
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
} |
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
} |
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 {
# ...
} |
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} {}
} |
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
} |
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なんて名乗るのはおこがましいので、もうちょい慎ましい名前を考えたら使ってみようと思う。
候補
Tclだとそんなところに脳のリソースを割かないといけないの?とか言わないでね。