Вопрос

Что не так в коде?

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

Я пытаюсь использовать доходность для печати, возьмите это, кроме десятого числа фибоначчи.

Я получаю сообщение об ошибке: в `call_block ': нет в блоке (localjumerror)

Даже следующий код бросает ошибку:

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"}
Это было полезно?

Решение

Возможно, вы захотите использовать эту строку, как Адам Ванденберг Подсказки:

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

Другие советы

Во-первых, давайте почистите это немного, так что легче увидеть, что идет не так:

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

Так, n является 10 И блок {ставится «взять это»}. С n ни однозначно 1 ни 2, мы приезжаем на yield, который передает контроль на блок.

Теперь мы называем

call_block(n-1)

который

call_block(9)

Обратите внимание, что мы не называем его блоком. Итак, для этого нового звонка, n является 9 И нет блока. Опять же, мы пропускаем первые две линии и приходите к yield.

Но нет блока до yield чтобы, и именно поэтому код поражает здесь.

Решение является очевидным, так и тонким. Очевидная часть: проблема в том, что мы не передаем блок, таким образом, решением нам необходимо пройти блок. Тонкое участие: как мы это делаем?

То, что делает рубиновые блоки настолько синтаксически легким, состоит в том, что они анонимны. Но если бы блок не имеет имени, мы не можем ссылаться на него, и если мы не можем ссылаться на него, то мы не можем пройти его вместе.

Решением для этого является использование другой конструкции в 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' }

Это из-за рекурсивного звонка к методу call_block без прохождения в блоке. Один из способов сделать это:

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

Редактировать: я должен признать, что решение Опубликовано справедливостью кажется более логичным.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top