ruby 从给定给instance_exec的块中提前返回

w46czmvw  于 2023-05-17  发布在  Ruby
关注(0)|答案(1)|浏览(114)

我需要允许在一个类的作用域内定义和调用块,使用instance_exec(通过Rails 2.3.2)。但是,其中一些块在某些情况下需要提前返回,这给我带来了一个问题。
我的应用程序是使用Ruby 1.8.6构建的,但我也需要让它在1.8.7上运行。在两个版本之间,似乎从lambda内部返回的能力被删除了。下面的代码在1.8.6中工作,但在1.8.7中抛出LocalJumpError(意外返回):

class Foo
  def square(n)
    n ** 2
  end

  def cube(n)
    n ** 3
  end

  def call_block(*args, &block)
    instance_exec *args, &block
  end
end

block = lambda { |n|
  return square(n) if n < 5
  cube(n)
}

f = Foo.new
f.call_block(5, &block) # returns 125
f.call_block(3, &block) # returns 9 in 1.8.6, throws a LocalJumpError in 1.8.7

我确定,如果我用next替换块中的return,我可以在1.8.7中让它工作,但在1.8.6中,next square(n) if n < 5会导致nil
有什么办法可以让它在1.8.6和1.8.7中都工作吗?我知道我可以重组我的块来使用分支而不是提前返回,但是有些块更复杂,并且有多种情况需要提前返回。
另外,如果我想让我的代码在ruby 1.9中运行,这会进一步改变吗?

**编辑:**我发现它在1.8.6而不是1.8.7中工作的原因是1.8.7在C源代码中定义了自己的instance_exec,而1.8.6使用Rails的实现。如果我用Rails的版本覆盖1.8.7中的instance_exec,它也能工作。

qmb5sa22

qmb5sa221#

评论后编辑详情请参见此post

class Foo
  
  def square(n)
    n ** 2
  end

  def cube(n)
    n ** 3
  end

  def call_block(*args, &block)
    instance_exec *args, &block
  end
end



def a
  block = lambda { | n|
    return square(n) if n < 5
    cube(n)
  }
 f = Foo.new 
 puts f.call_block(3, &block) # returns 125
 puts "Never makes it here in 1.8.7"
 puts f.call_block(5, &block) # returns 9 in 1.8.6, returns nothing in 1.8.7
end

a

这段代码没有任何结果,因为它在puts语句之外返回。
procs和lambdas的工作方式在1.9中改变了。这有助于解释发生了什么。

原件

我重构了你的代码,它在所有3个虚拟机下工作。有趣的是,您的代码在1.9下执行时没有异常。

class Foo
  
  def square(n)
    n ** 2
  end

  def cube(n)
    n ** 3
  end

  def call_block(*args, &block)
    block.call(self, *args)
  end
end

block = lambda { |obj, n|
  return obj.square(n) if n < 5
  obj.cube(n)
}

f = Foo.new
puts f.call_block(5, &block) # returns 125
puts f.call_block(3, &block) # returns 9

post可能会有一些见解。

相关问题