Ruby块的问题

Yah*_*-Me 2 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"}
Run Code Online (Sandbox Code Playgroud)

我正在尝试使用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"}
Run Code Online (Sandbox Code Playgroud)

Jör*_*tag 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' }
Run Code Online (Sandbox Code Playgroud)

现在让我们来看看它.

我们先打电话来

call_block(10) { puts 'Take this' }
Run Code Online (Sandbox Code Playgroud)

所以,nis 10和block是{puts'Take this'}.因为n既不是也不12,我们到达yield,它将控制转移到块.

现在我们打电话

call_block(n-1)
Run Code Online (Sandbox Code Playgroud)

是的

call_block(9)
Run Code Online (Sandbox Code Playgroud)

请注意,我们没有用块调用它.所以,对于这个新的电话,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' })
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,这语法上有点重,但我们可以给出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' }
Run Code Online (Sandbox Code Playgroud)