Ruby の proc と lambda!

proc の中で return すると、呼び出し元のメソッドも return しちゃう。lambda の中で return しても、呼び出し元のメソッドは処理を続行する。という風に丸暗記してます。ほんとにそうなのか確かめてみました:

#!/usr/bin/env ruby
# coding: utf-8

def foo
  proc do
    return "hoge"
  end.call

  "piyo"
end

def bar
  lambda do
    return "hoge"
  end.call

  "piyo"
end

p foo
p bar

以下実行結果です:

"hoge"
"piyo"

proc と lambda、それぞれのクラスは何でしょうか:

#!/usr/bin/env ruby
# coding: utf-8

p proc{}.class
p lambda{}.class

どちらも Proc クラスです:

Proc
Proc

なお、以下のようにすると LocalJumpError になります:

#!/usr/bin/env ruby
# coding: utf-8

class LocalJumpError
  # http://route477.net/d/?date=20060925
  def p
    puts "#{backtrace.shift}: #{message} (#{self.class})"
    puts backtrace.map{|s| "\tfrom #{s}"}.join("\n")
  end
end
 
def f
  yield
  "piyo"
end
 
foo = proc { return "hoge" }
bar = lambda { return "hoge" }
 
begin
  p f(&foo)
rescue LocalJumpError => e
  e.p
end

begin
  p f(&bar)
rescue LocalJumpError => e
  e.p
end

実行結果:

hoge.rb:16:in `block in <main>': unexpected return (LocalJumpError)
	from hoge.rb:12:in `f'
	from hoge.rb:20:in `<main>'
hoge.rb:17:in `block in <main>': unexpected return (LocalJumpError)
	from hoge.rb:12:in `f'
	from hoge.rb:26:in `<main>'

main で return しようとした、ということのようです。
ところで、lambda でも LocalJumpError になってるのはなぜ?ちょっと試したところ、以下のように書けばいいようです:

def f &b
  b.call
  "piyo"
end

なお、proc の LocalJumpError は以下のように書きなおすと発生しません (http://docs.ruby-lang.org/ja/2.0.0/class/Proc.html):

#!/usr/bin/env ruby
# coding: utf-8

def f &b
  b.call
  "piyo"
end

foo = proc do
  next "hoge"
end

bar = lambda do
  return "hoge"
end

p f(&foo)
p f(&bar)

実行結果:

"piyo"
"piyo"

でも、メソッドからの脱出ができなくなりました。
苦心の末、荒業を編み出しました:

#!/usr/bin/env ruby
# coding: utf-8

def f &b
  begin
    b.call
  rescue LocalJumpError => e
    return e.exit_value
  end

  "piyo"
end

foo = proc do
  return "hoge"
end

bar = lambda do
  return "hoge"
end

p f(&foo)
p f(&bar)

実行結果です:

"hoge"
"piyo"

いったい何がしたいんだ!と自問自答する日々です。

(コウヅ)

広告

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中