2

コードのどこが間違っていますか?

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"}

yield を使用して印刷しようとしています これを 10 番目のフィボナッチ数以外にしてください。

エラーが発生しています: in `call_block': no block given (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"}
4

3 に答える 3

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{ puts 'Take this' } です。nは も1でもないので、制御をブロックに移す2に到達します。yield

今、私たちは呼んでいます

call_block(n-1)

これは

call_block(9)

ブロックで呼び出していないことに注意してください。したがって、この新しい呼び出しでn9ブロックはありません。繰り返しますが、最初の 2 行をスキップしてyield.

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

解決策は明らかであり、巧妙です。明らかな部分は次のとおりです。問題はブロックを渡していないことです。したがって、解決策はブロックを渡す必要があることです。微妙な部分は次のとおりです。どうすればそれを行うことができますか?

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

これに対する解決策は、Ruby で別の構造を使用することです。これは基本的に、ブロックよりも「コードの塊」の概念をより重く抽象化したものです: a 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 では特別にサポートされています。&パラメータリストのパラメータ名の前にシジルを置くと、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' }
于 2010-11-23T20:46:57.850 に答える
3

Adam Vandenbergが示唆しているように、次の行を使用することをお勧めします。

return call_block(n-1) { yield } + call_block(n-2) { yield }
于 2010-11-23T18:58:40.267 に答える
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"}

編集:私は正義によって投稿された解決策がより論理的に見えることを認めなければなりません。

于 2010-11-23T19:08:33.393 に答える