新手问题 Proc 以内变量继承外部变量的问题

u4crella · 2020年03月27日 · 最后由 oatw 回复于 2020年03月31日 · 2873 次阅读

如图,对于“x = 0”这一语句,注释第 7 行而允许执行第 16 行,可以看到执行完线程后 x 的值是 0;如果注释掉第 16 行而允许执行第 7 行的话,执行完线程后 x 的值是 6。

也就是说 Proc.new 在被定义的时候就已经查找好了在 Proc 里面的变量的可能的继承关系,而不是等这个 proc 在被执行的时候再去查找继承关系吗?

手头没有电脑,只能这样发贴,请见谅。

x = 1
pr = Proc.new do
  begin
    x
  rescue NameError
   puts '未定义x'
  end
  begin
    y
  rescue NameError
   puts '未定义y'
  end
end
y = 2
puts 'proc定义完毕'
pr.call

# 结果是: 未定义y

好像是这样,感觉这样有点奇怪啊,哪位可以解释一下定义 Proc 的时候都做了些什么?

有点疑问。比较 ruby 和 python3 的闭包:

x = 1
pr = Proc.new do
   (x+y)
end
y = 2
puts 'proc定义完毕'
puts pr.call
x = 1
lam = lambda { return (x+y) }
y = 2
puts lam.call
# python3 闭包1

x = 1
def f():
  def g():
    return (x+y)
  return g()

y = 2
print(f())
# python3 闭包2

x = 1
lam = lambda : (x+y)
y = 2
print(lam())

可见 python3 的闭包内的变量是在闭包块在执行时才查找变量继承关系的;而 ruby 是在闭包被定义的时候就查找变量继承关系的。

请问 ruby 这样做是考虑到什么了吗?因为我写代码的时候习惯先写定处理逻辑,再设定变量值,这样在 ruby 里写闭包,就可能不小心踩到坑了。

看来是我对闭包理解错误。 python3、javascript都是在执行时才检查闭包内变量,而ruby、php5、golang、c#都是在定义时已检查闭包内容的。

u4crella 回复

你的结论是对的。

这个是《Ruby 元编程》里的解释:

You’re probably wondering where the block picks up its bindings. When you define the block, it simply grabs the bindings that are there at that moment, and then it carries those bindings along when you pass the block into a method.

补充一哈交流交流,本质上应该是变量作用域的问题吧~ Ruby 中的 block 和 proc 应该都是能够实现扁平作用域和共享作用域的方式,如果在其外已经定义了 local variable,那么在其内是可以共享到定义变量的 scope 的,但是如果没有在其外定义而是在其内定义,那么这个内部定义的 local variable 的 scope 只在 block 或 proc 内。所以就有了上面你说的不同位置定义 x 最终值不同的问题。

关于上下文环境绑定的问题,类似机制应该不局限于闭包,来段《Ruby 元编程》中抄来的代码。

class MyClass
  def my_method
    "original my_method()"
  end

  def another_method
    my_method  # using has not been called, so still the original my_method
  end
end

module MyClassRefinement
  refine MyClass do
    def my_method
      "refined my_method()"
    end
  end
end

using MyClassRefinement
MyClass.new.my_method       # => "refined my_method()"
MyClass.new.another_method  # => "original my_method()"

感觉 Ruby 对于上下文环境的绑定是在定义时就具体到了被引用的变量和对象等。而对于像 javascript 这样的语言,像是只绑定了一个上下文环境指针,执行的时候再从这个上下文环境中再查找对应的变量、对象等。不知道猜得对不对。

需要 登录 后方可回复, 如果你还没有账号请 注册新账号