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

我正在尝试使用产量来打印除第十个斐波那契数之外的其他值。

我收到错误:在“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"}
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)

请注意,我们不是用块调用它。所以,对于这个新的调用,nis9并且没有阻塞。同样,我们跳过前两行并来到yield.

但是没有阻止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这是因为在没有传入块的情况下递归调用该方法。一种方法是:

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

编辑:我必须承认,Justice 发布的解决方案似乎更合乎逻辑。

于 2010-11-23T19:08:33.393 回答