株の話、まったく関係ないが、
理解できずに先に進むのも気持ち悪い、
たぶん、Rubyのthrow/catchは昔馴染んだtry/catch構文とは違うはずだ。
ここで、
なんで、これで、no_valueがthrowされてもスルーできるんか、理解する。
def with_valid_indicators
catch(:no_value) {yield}
end
分からんので、グーグル先生に訊く。
なかなか、良い解説に巡り会えないが、あ、あった。
誤解されている 6 つの Ruby の機能の真相を知る
http://www.ibm.com/developerworks/jp/opensource/library/os-sixrubyfeatures/
Ruby での throw および catch 機能は、本当の意味での例外処理ではありません。throw は、プログラムのフローを変えるために使用することができます。
うん、Goto文だと思えと、聞いた。
throw 文に辿り着くと、コードの実行が中断され、インタープリターが対応するシンボルを処理する catch ブロックの検索を開始します。catch ブロックが終了すると、そこから実行が再開されます。
うわあ。なんか気持ち悪い。
要するに、catchにジャンブするだけで、例外処理のtry/catch構文とは関係ない。
一部の人々は「Ruby は C の goto 文の狂気を受け継いで、throw と catch のサポートによる全く新しい高みへと引き上げた」とまで言っています。
まったくです。Rubyのこういうところがなぁ。
何で紛らわしい名前ばかりつけるのだろう、同じ機能を別の名前にしたり、
ホント嫌いだ。
テストしてみました。
def throw_fnc
puts “throw_fnc”
throw :aaa
end
def throw_catch_fnc
puts “start”
throw :aaa
puts “after throw”
catch(:aaa){
puts “catch”
}
puts “out of catch”
end
これだと、throw_fncを呼んで、catchされず、エラーで終了です。
一方、
def catch_fnc
puts “catch_fnc”
catch (:aaa) {
puts “catch”
throw_fnc
puts “after throw_fnc”
}
puts “out of catch”
end
だと、throw_fncを呼んで、after throw_fncに届かず、out of catchに移動します。
要は、内部でthrowがあれば、強制的にcatch句の外に出る。
マニュアルにもその通りの記述。
https://docs.ruby-lang.org/ja/latest/method/Kernel/m/throw.html
Kernel.#catchとの組み合わせで大域脱出を行います。 throw は同じ tag を指定した catch のブロックの終わりまでジャンプします。
breakだと、ループを抜けるだけだが、
多重にブロックが入れ子構造になっていても、catchで囲めば、throwで外に出られる。
ただそれだけです。
だから以下の内容は、
def with_valid_indicators
catch(:no_value) {yield}
end
処理ブロックが渡されればそれが実行され、
処理ブロックの中で、no_valueがthrowされていれば、外に出る。
ただし、外側には処理が何も無いので、その時点で終了。
ググっても、教科書的な例外処理の説明ばかりヒットし、
catch のブロックの終わりまでジャンプします。
この部分を解説してくれるもの少ない。
throw/catchは大域脱出の仕組みであり、例外処理としても使える。
という理解が適切なようです。
好ましくないのは、例外発生で大域脱出が出来るという考え方で、
例外は通常でないエラー処理であって、
gotoの目的で例外発生させるのは、本末転倒でしょう。
そういうのが、「楽しい」ってのは思想的には受け付けないなぁ。
その楽しさが優先すべきこととは思えない。
ともかくも、
catchはそのネストからの大域脱出である。と理解出来たことで、
Ruleクラスでのnilを無視する仕組みも理解できた。
質問コーナー、お問い合わせは、sanpome.net@gmail.com まで。