[#47955] るびま記事募集:FiberとかSinatra/PadrinoとかBundlerとか — Makoto Kuwata <kwa@...>

桑田といいます。るびま編集部に入りました。

17 messages 2011/04/11

[#48016] 文字列を正規表現にマッチする部分と他の部分に分割 — "5.5" <5.5@...>

5.5 です。

12 messages 2011/04/28

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

From: "Y. NOBUOKA" <nobuoka@...>
Date: 2011-04-22 13:24:37 UTC
List: ruby-list #47998
信岡です。

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

・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/

In This Thread