Ruby块的问题

时间:2010-11-23 18:54:04

标签: ruby yield block

代码有什么问题?

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来打印除了第十个斐波那契数字之外的其他内容。

我收到错误: 在`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 个答案:

答案 0 :(得分: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并且没有阻止。同样,我们跳过前两行并转到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中有特殊的支持。如果在参数列表中的参数名称前加上& sigil,Ruby将“打包”一个作为参数传递给Proc对象的块并将其绑定到该名称。相反,如果你在参数列表中的参数表达式前放置一个& sigil,它会将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' }

答案 1 :(得分:3)

您可能希望将此行用作Adam Vandenberg提示:

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

答案 2 :(得分: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"}

编辑:我必须承认解决方案posted by Justice似乎更符合逻辑。