2011年4月22日

[ruby-list:47998] Re: lambda中のProc中のreturnの挙動について

信岡です。

私も少し悩みましたが、以下のように考えるとわかりやすいのではないでしょうか。

・return-expression (return 式) は 「メソッドの実行を終了させる」
・lambda は 「メソッドではない」
・Ruby 1.9 では、メソッドにもブロックにも含まれない lambda を、return 式の挙動に
関してメソッドのように扱う (?)


Ruby の JIS 規格 [1] に書かれていると思う [2] のですが、
return 式は、自身を含んでいる実行中のメソッド本体を終了させるというものです。
そのようなメソッドがない場合は LocalJumpError が発生します。
(return 式の項をご覧ください。 Draft 版であれば 11.5.2.4.2 節。)
その際、ブロックが入れ子構造になっていた場合、内側のブロックから順に
全てのブロック本体の実行も停止させます。

また、もう一点重要なこととしては、lambda は proc と比べてメソッドに近い動きを
するものの、メソッドではない、ということです。

[1] JIS X 3017 :
http://www.ipa.go.jp/software/open/ossc/english/ruby/ruby_draft_specification.html
[2] 私の環境では JIS の閲覧がなぜかできないので、私は Draft 版を参照しています :
http://www.ipa.go.jp/software/open/ossc/english/ruby/ruby_draft_specification.html

> a = lambda do
> b = Proc.new do
> return
> end
> b[]
> end
> a[]
>
> 上記のコードを実行すると、ruby1.8.7ではLocalJumpErrorが起こり、ruby1.9.1では正常終了します。
> lambda中のreturnはcallのreturnになり、Proc.new中のreturnは生成元のreturnになる筈なので、
> 正常終了が正しい挙動のように思います。

上の例の場合、return 式は lambda の中の proc に含まれていますが、メソッドの中には
入っていません。 よって、return 式が評価された際には修了させるべきメソッドが存在しません
ので、LocalJumpError が発生します。 (1.8 の場合)
Ruby 1.9 で LocalJumpError が発生しないのは、メソッドやブロックに含まれていない lambda の中で
return 式が実行された場合は、その lambda の実行を終了させるようになったからだと思われます。
(lambda がより メソッドに近い挙動をするようになった。)
ただ、この変更についてのドキュメントを探してみましたが見つからないので、
もしご存知の方がいらっしゃれば教えて頂きたいです。

> また、
>
> def a
> b = lambda do
> Proc.new do
> return
> end
> end
> b[][]
> end
> a
>
> を実行すると、ruby1.8.7、ruby1.9.1ともに正常終了します。
> こちらは、Proc.new中のreturnが実行されるときには生成元が既に終了しているので、LocalJumpErrorが起こるのが正しい挙動のように思います。

これも同様で、return 式は lambda を終了させるのではく、メソッド a を終了させます。
そう考えると、b[][] が実行される時点ではまだメソッドは終了していませんので、
LocalJumpError は起こらないのだと考えられます。


私も制御フローに関して詳しくないので間違いがあればツッコミをお願いします。
以下に、私が制御フローの確認をするために使ったコードを書いておきます。
もし何かの参考になれば。

# return 式によって lambda が終了されるのではなく
# メソッドの実行が終了されるということの確認
def test1
b = lambda do
Proc.new do
Proc.new do
return :aa
end
end
end
$stdout << "test1-1 : #{b[][][]}\n" # Ruby 1.8, 1.9 ともにこの文字列は出力されない
end
$stdout << "test1-2 : #{test1()}\n" # Ruby 1.8, 1.9 ともにこの文字列は出力される

# メソッドに含まれない lambda の中で return 式が実行された際に
# Ruby 1.8 と 1.9 で動作が異なるということの確認
test2 = lambda do
b = lambda do
Proc.new do
return :aa # Ruby 1.8 ではこの return 式で LocalJumpError 発生
end
end
$stdout << "test2-1 : #{b[][]}\n" # Ruby 1.9 ではこの文字列は出力されない
end
$stdout << "test2-2 : #{test2[]}\n" # Ruby 1.9 ではこの文字列は出力される

--
信岡 ゆう (NOBUOKA Yu)
http://www.vividcode.info/


投稿者 xml-rpc : 2011年4月22日 22:24
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/103213
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。