2012-01-17 5 views
7

The Ruby Programming Languageあたりなど)のブロックのbreak文が定義されていますProc.new v。アンパサンド記号を使用すると、rubyのbreak文の動作が異なるのはなぜですか?次のように

は、そのイテレータ、それを呼び出したメソッドに戻りイテレータに戻るには、ブロックの原因となります。

したがって、次のコードを実行すると、LocalJumpErrorが発生します。

def test 
    puts "entering test method" 
    proc = Proc.new { puts "entering proc"; break } 
    proc.call # LocalJumpError: iterator has already returned 
    puts "exiting test method" 
end 
test 

次のコードはLocalJumpErrorをスローしませんが。アンパサンド記号について特別なものは何ですか?アンパサンド記号が暗黙的にProc.newを使用していませんか?

def iterator(&proc) 
    puts "entering iterator" 
    proc.call # invoke the proc 
    puts "exiting iterator" # Never executed if the proc breaks 
end 

def test 
    iterator { puts "entering proc"; break } 
end 
test 

つまり、私はProc.new呼び出しのインライン展開の手段としてアンパサンド記号を読みました。その時点での動作は、最初のコードスニペットと同じにする必要があります。

def iterator (p = Proc.new { puts "entering proc"; break}) 
... 
end 

免責事項:私は初心者くさい(ルビー1.9.2)の言語を勉強していますので、参照して、詳細な概要を理解するだろう。

+1

適切な答えはありませんが、これはすべて範囲に関するもので、ProcやLambdaは特別なものではありません。 – coreyward

+0

あなたが時間を得るとき...この質問に再度訪問してください。あなたの洞察に感謝します –

+0

'Proc.new'の代わりに' lambda'を試してみてください。 – Casper

答えて

6

breakはブロックブロックリターンの呼び出し側になります。次のコードでは:

proc = Proc.new { break } 

Procオブジェクトに変換されるブロックの「呼び出し元」はProc.newです。 breakは、ブロックの呼び出し元を返すが、Proc.newはすでに返されているはずである。このコードで

def iterator(&b); b.call; end 
iterator { break } 

ブロックの呼び出し元がiteratorので、それはiteratorリターンします。

+0

これは最高の答えです。しかし、Proc.newを介してすべてのブロックが実行可能なコードに変換されているわけではありませんか?したがってイテレータ(&b)はイテレータにならない(b = Proc.new b)? –

+0

&Proc.newは同じではありません。 &はコア構文です。 Proc.newはライブラリメソッドです。次のように独自のProc.newを書くことができます: 'class Proc; def self.new(&b);b; end; end')しかし、(インタプリタのハッキングやプリプロセッサの使用を除いて)独自のコア構文を実装する方法はありません。 –

3

ここにはanswerがあります。

アンパサンドは、procをブロックに変換し、ブロックをprocに変換するために使用されます。

あなたのケースに関連するように、私は例を変更:

def run_my_code(&my_code) 
puts 'before proc' 
my_code.call 
puts 'after proc' 
end 
run_my_code { puts "passing a block, accepting a proc"; break} 
=> before proc 
    passing a block, accepting a proc 

あなたはそれはあなたが持っているあなたの2番目の例では「procの後に」

def run_my_code 
yield 
end 
my_proc = Proc.new { puts "passing a proc instead of block"; break} 
run_my_code &my_proc 
=> passing a proc instead of block 
    LocalJumpError: break from proc-closure 
    from (pry):75:in `block in <main>' 

に到達しなかった見ることができるように結果としてprocが発生すると、procはiteratorから復帰し、test関数に戻ります。

def iterator(&proc) 
    puts 'entering iterator' 
    proc.call 
    puts 'exiting iterator' 
end 

def test 
    puts 'before test' 
    iterator { puts 'entering proc'; break } 
    puts 'after test' 
end 

=>before test 
entering iterator 
entering proc 
after test 
+0

私はこれが正しい場合、最初の例では、breakステートメントはProc.newから返されます(iteratorとして)とそれを囲んでいるブロックから。私は理解したように質問の冒頭に定義を文書化しました。なぜ最初のものが失敗したのですか?しかし、2番目の例でアンパサンド記号がProc.newの構文的砂糖である場合、私は理解しません。なぜ失敗しないのですか? –

+0

私は答えを修正しました。 – megas

+0

ブロックがprocに変換された場合、breakステートメントは最初のコードスニペットと同様に機能しませんか?つまり、基本的にProc.newをインライン展開すると、メソッド呼び出しの上に別の行にあるProc.newとは動作が異なるのはなぜですか? –

0

ブロックとプロキシとラムダの違いとそれぞれのスコープが関係しています。

私は戻って2009年にあなたが役立つかもしれないと、それについての記事を書いた:http://www.leonardoborges.com/writings/2009/07/22/procs-lambdas-blocks-whats-the-difference/

・ホープ、このことができますし。

+0

ポストははっきりと洞察力がありましたが、両方のインスタンスでprocを使用していることを考えると、なぜRubyが別のプロシージャに特別な処理を施そうとしているのかわかりません。 –

+0

投稿の返信キーワードの説明と関係があります。この場合の戻り値などのブレークは、この場合は呼び出し側のメソッドtestからのブレークを意味します。しかし、Proc.new {...}の直後に改行を入れることで検証できるので、テストから壊れることはできません。これは、2番目のスニペットでは呼び出しメソッド(この場合はイテレータ)から戻るため、新しいバージョンのテストに制御を戻します。 – leonardoborges

+0

testがtest_testというメソッドでラップされている場合、Proc.newのbreakステートメントは失敗しませんか? ... はその場合、LocalJumpErrorは単にテストのように、起こるべき端; Iは、PROC変換 DEFイテレータ(&proc); #implicityコールPROC = Proc.new {PROC}のブロックを読んでいる方法ここ 」。 –

関連する問題