【问题标题】:Weird `return` behaviour when switching from matz ruby to jruby从 matz ruby​​ 切换到 jruby 时出现奇怪的“return”行为
【发布时间】:2014-09-19 21:19:47
【问题描述】:

为什么会出现这个sn-p:

def dump_dump
    get_dump = lambda do
        return 1 if $n
        $n = true
        module_exec &get_dump
        2
    end
    p get_dump[]
end

Module.new do
    module_exec &method(:dump_dump)
end

在 ruby​​ 2.0.0p481 (2014-05-08) [x64-mingw32] 中打印 2
但是 1 在 Java HotSpot(TM) 64 位服务器 VM 上的 jruby 1.7.15 (1.9.3p392) 2014-09-03 82b5cc3 1.7.0_67-b01 +jit [Windows 8-amd64]?

我想了解这个问题。

UPD:是否应该在某处报告?

【问题讨论】:

    标签: ruby jruby behavior incompatibility


    【解决方案1】:

    我一直认为块内的return 是未定义的行为。你可以改用next 吗?

    例如,Rubinius 也有这个问题,但要明确得多:

    [1].map(&lambda { |n| return -1 })
    LocalJumpError: unexpected return
    

    当然,使用next 会产生预期的结果:

    rbx-head :003 > [1].map(&lambda { |n| next -1 })
     => [-1] 
    

    这个故事的寓意是 return 是为方法定义的,而 Procs 和 lambdas 不是方法。 nextbreak 是要停止块调用时使用的关键字。

    我在官方 Ruby 规范中找不到关于 return 行为的任何文档,但 rubyspec 确实有测试验证 return 导致调用方法返回。

    https://github.com/rubyspec/rubyspec/blob/master/language/return_spec.rb#L184

    【讨论】:

    • 块不应该处理返回,只有周围的方法。然而 lambda 应该有方法语义。转换为块时,lambda 保留其 lambda 状态,所以....这就是我找到的所有参考gist.github.com/mislav/4508988
    • 我刚刚测试了这个。在 MRI 中,lambda 在调用to_proc 时保持其 lambda 属性。另一方面,在 rbx 中,lambda 属性会丢失(例如,丢失的方法不会引发错误)。
    【解决方案2】:

    lambda 中的“return”应该从 lambda 返回,而不是从方法返回。 在这个棘手的情况下,看起来 jruby 不尊重内部 lambda,而是一直返回到第一个 lambda 调用。

    起初我认为这可能是由 lambda 中的 lambda 调用引起的,但现在我认为这是与块转换相关的问题,将示例简化为:

    Module.new do
      test = lambda do
        return
      end
      module_exec &test
      puts 'after'
    end
    

    这里只有 mri 打印 'after',而 jruby 什么也不打印。

    ...但是如果我们不做 lambda 来阻止转换(&test):

    Module.new do
      test = lambda do
        return
      end
      module_exec { test[] }
      puts 'after'
    end
    

    mri 和 jruby 都打印 'after'...

    【讨论】:

    • 您确定没有将它们相互替换吗?我的 jruby 通过所有调用返回——这就是我发现问题的方式。原始代码是遍历一棵树并且必须返回一个长向量,但是 jruby 将我抛出 nil,在我的程序中进一步引发异常。
    • 我记得,当我尝试在我的原始代码中使用 { test[] } 而不是 &test 时,它有另一个作用域并且没有看到模块方法等,所以这个替换是'不等价。
    • true -- 我试图找出原因。试试这个:def test; [1].map(&lambda{ |n| return -1 });结束
    • 我刚刚将此添加到 jruby 问题跟踪器:github.com/jruby/jruby/issues/1985
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-01
    • 2015-02-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-15
    相关资源
    最近更新 更多