首先,让我们清理一下了一点,所以它更容易看到什么错:
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块在语法上轻量级的原因是它们是匿名的。但是如果这个块没有名字,我们就不能引用它,如果我们不能引用它,那么我们就不能传递它。
对此的解决方案是在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
成块:
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方法中的yield语句不同。 – Bruce 2011-09-28 22:15:18
`yield`调用传入当前执行方法的block/proc参数槽的块或proc。 – yfeldblum 2011-09-28 22:57:49