2010-11-23 65 views
2

代码有什么问题?Ruby块的问题

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

你可能想使用此行,为Adam Vandenberg提示:

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

那些“屈服”了,否则空块内声明 - 这块他们屈服于?我很困惑,我认为这些块本身被调用是因为call_block方法中的yield语句不同。 – Bruce 2011-09-28 22:15:18

+0

`yield`调用传入当前执行方法的block/proc参数槽的块或proc。 – yfeldblum 2011-09-28 22:57:49

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似乎更符合逻辑。

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和块{放 '拿这个'}。由于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成块:

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