Ruby 有办法改变 lambda 运行时的上下文吗

tinyfeng · 2021年02月22日 · 最后由 tinyfeng 回复于 2021年02月23日 · 449 次阅读

有两段代码

a = 1
l = -> { puts a }
l.call
l = -> { puts b }
b = 1
l.call

其中第一段代码正常,而第二段代码会抛异常,因为声明 l 的时候上下文并没有 a。

原理应该类似 eval 不能在上下文中声明一个局部变量,l 运行时的上下文是声明时的上下文,不随外部上下文改变而改变,那有没有办法让第二段代码运行起来呢

ruby 中估计没有能让第二种代码运行的办法,有这种办法俺也不用。

在 python 和 js 中,第二种代码是能正常运行。然而这种特性,估计也只有面试能用到...

这种不知道是否能满足楼主需求

l = -> (ctx) { eval('puts b', ctx) }
b = 1
l.call(binding)

这个是因为 lambda 在创建的时候已经编译好了,用 RubyVM::InstructionSequence.disasm 可以看 lambda 编译后的字节码,会发现如果当时有变量字节码是读取 local,否则是调用方法。所以不是作用域的问题,在其他脚本语言里也没有。

这个问题无法避免,可以使用实例变量等等来绕过。

mizuhashi 回复

感谢提示,换个思路,把局部变量换成方法,问题解决了。

l = -> { puts c }
define_method(:c) { 3 }
l.call

ps: 通过实例变量可以绕过这个我知道,但是写起来就不太雅观~

tinyfeng 关闭了讨论。 02月23日 14:50
需要 登录 后方可回复, 如果你还没有账号请 注册新账号