2010-11-23 14 views
2

コードで何が問題になっていますか?Rubyブロックの問題

def call_block(n) 

    if n==1 

    return 0 
    elsif n== 2 

    return 1 
    else 
    yield 
    return call_block(n-1) + call_block(n-2) 

    end 

end 


puts call_block(10) {puts "Take this"} 

私は印刷するために歩留まりを使用しようとしています。フィボナッチの10番以外の番号を取ってください。

私はエラーを取得しています: `call_block '内の :(LocalJumpError)与えられた何ブロックは

であっても、次のコードは、エラーをスローしません:

def call_block(n) 

    if n==1 
    yield 
    return 0 
    elsif n== 2 
    yield 
    return 1 
    else 
    yield 
    return call_block(n-1) + call_block(n-2) 

    end 

end 


puts call_block(10) {puts "Take this"} 

答えて

3

あなたはAdam Vandenbergとして、このラインを使用する場合がありますヒント:

return call_block(n-1) { yield } + call_block(n-2) { yield } 
+0

それらの「歩留まり」文 - どのブロックは彼らに降伏ですか?私は混乱しています。私は、call_blockメソッド内の異なるyield文のために、それらのブロック自体が呼び出されたと考えました。 – Bruce

+0

'yield'は、現在実行中のメソッドのブロック/ proc引数スロットに渡されたブロックまたはprocを呼び出します。 – yfeldblum

+0

もっと正確に言えば、 'yield'はブロック/ proc引数スロットのメソッドに渡されたブロックまたはprocを呼び出します。このメソッドでは' yield'が字句的に表示されます。 – yfeldblum

1

ため、ブロックに渡すことなく、方法call_blockへの再帰呼び出しのです。それを行う1つの方法は、次のようになります。

def call_block(n, &blk) 
    if n == 1 
     return 0 
    elsif n == 2 
     return 1 
    else 
     blk.call() 
     return call_block(n-1, &blk) + call_block(n-2, &blk) 
    end 
end 

puts call_block(4) {puts "Take this"} 

編集:私は解決策posted by Justiceがより理にかなっていることを認めなければなりません。

7

まず、それは間違っている何が起こっているのか確認するために簡単になりましたように、のは少しそれをクリーンアップしてみましょう:

def call_block(n) 
    return 0 if n == 1 
    return 1 if n == 2 

    yield 

    call_block(n-1) + call_block(n-2) 
end 

puts call_block(10) { puts 'Take this' } 

今度はそれを通じトレースしてみましょう。

私たちは

call_block(10) { puts 'Take this' } 

を呼び出すことによって開始し、n10で、ブロックは、{プットが 'これを取る'}です。 n1でも2でもないので、ブロックに制御を移すyieldに到着します。

は今、我々はブロックとそれを呼び出していません

call_block(9) 

お知らせです

call_block(n-1) 

を呼んでいます。したがって、この新しい呼び出しでは、n9であり、ブロックはありません。再び、最初の2行をスキップしてyieldに来ます。

しかし、yieldにはブロックがありません。そのため、ここでコードが爆発的になります。

解決策は明らかで微妙です。明らかな部分は、問題はブロックを通過していないということです。したがって、解決策はブロックを渡す必要があることです。微妙な部分は、どうすればいいのでしょうか?

Rubyブロックを構文的に軽量にするのは、それらが匿名であることです。しかし、ブロックに名前がなければ、それを参照することはできません。参照できない場合は、それを渡すことはできません。

これに対する解決策は、基本的にはブロックよりも「コードの塊」というアイデアのためのより重い抽象化であるRubyの別の構造を使用することです:Proc。あなたが見ることができるように

def call_block(n, blk) 
    return 0 if n == 1 
    return 1 if n == 2 

    blk.() 

    call_block(n-1, blk) + call_block(n-2, blk) 
end 

puts call_block(10, ->{ puts 'Take this' }) 

、このは少し重い構文的ですが、私たちはProcに名前を付けるため、再帰呼び出しに沿って、それを渡すことができます。

しかし、このパターンは実際にはRubyの特別なサポートがあるほど一般的です。パラメータリストのパラメータ名の前に& sigilを置くと、Rubyは引数として渡されたブロックをProcオブジェクトに「パッケージ化」し、その名前にバインドします。そして、あなたは引数リストの引数の式の前に&印章を置けば、逆に、それはブロックにProcことを「解凍」します:それ以外の場合は、空のブロックの内部

def call_block(n, &blk) 
    return 0 if n == 1 
    return 1 if n == 2 

    yield # or `blk.()`, whichever you prefer 

    call_block(n-1, &blk) + call_block(n-2, &blk) 
end 

puts call_block(10) { puts 'Take this' } 
+0

ジャスティスの答えは、この場合、「それをブロックに沿って渡す」ことができることを示しています。 –

+0

+1何が起こっているのか、OPのコードを改善する方法について説明します。 –

+0

@Andrew Grimm:それはブロックに沿って渡っていません。それはまったく新しい独立した2つの異なるブロックを通過しており、元のブロックを呼び出すだけです。 stacktracesを見ると、その違いをはっきりと見ることができます。ただ例外を強制する: 'a = 0;あなたは私の場合、ブロックが1つしかなく、スタックの深さははるかに小さいのですが、Justiceのバージョンでは、スタックの深いスタックがあります。ブロックをメソッドのスタックの上に配置します。私はまた、すべてのブロック相対制御フローが適切に機能することを完全には確信していません。 –