新手问题 偶然发现 ruby 闭包的一个问题

Niatruc · 发布于 2016年12月30日 · 最后由 Niatruc 回复于 2016年12月31日 · 722 次阅读
96

刚刚偶然发现ruby闭包的一个问题,我的代码简化后如下,

i=0
[:f1,:f2].each do|f|
    define_method(f) do
        j = i
        p j
    end
    i += 10
end

我想让f1和f2两个方法使用环境中的变量i,期待的结果是这样的:

f1 #=> 0
f2 #=> 10

真正的结果却是这样的:

f1 #=> 10
f2 #=> 10

莫非这意味着定义f1,f2这两个方法时使用的块共享了堆中同一片变量空间(其中包含变量j)? 有没有深入了解ruby解释器的伙伴解释解释呗~如果我想达到我要的结果,有没有好的写法?

共收到 11 条回复
96

不懂怎样才会有

f1 #=> 0
f2 #=> 20

这样的效果。但是我实现了这样的效果

f1 #=> 0
f2 #=> 10
i=0
[:f1,:f2].each do|f|
  j = i
  define_method(f) do
    p j
  end
  i += 10
end

2楼 已删除
96

#1楼 @qinix 老眼昏花写错了,结果就是你写的那样~

96
ruby中一切都是对象
i=0
[:f1,:f2].each do|f|
    define_method(f) do
        j = i
        p j
    end
    i += 10
end
p f1.object_id
p f2.object_id

看完这个你就懂了。

96

#1楼 @qinix 好吧。我想的太简单了,确实不懂,为啥i的object_id在第二种情况下会变,第一种又不会变呢?

96

嗯嗯,f1,f2虽然id不同,但是它们共享了do j=i; p j end这个闭包,所以也就共享了i这个变量。可以参考一下《ruby under the microscope》这本书

96

#5楼 @Niatruc 里头关于元编程的闭包问题讲得挺多

96

#6楼 @Niatruc 没懂你的意思,第一种情况f1,f2 object_id一样呀。第二种情况不同。然后我又进一步分析了一下。

i=0
p i.object_id
[:f1,:f2].each do|f|
    define_method(f) do
        j = i
        p j.object_id
        p i.object_id
        j
    end
    i += 10
    p i.object_id
end
p f1.object_id  #=> 1,21,41,41,41,41

第二种情况:

i=0
p i.object_id
[:f1,:f2].each do|f|
  j = i
  p j.object_id
  p i.object_id
  define_method(f) do
    j
  end
  i += 10
  p i.object_id
 end
p f1.object_id   #=> 1,1,1,21,21,21,41,1

为什么这两种情况代码运行的次数都不一致?

96

#7楼 @Niatruc 噢,我懂了,第一种情况共享的是j这个变量,而j这个变量跟i是同一个对象。但是第二种情况并没有共享变量,j在代码块之前已被赋值。所以不一致。

96

#6楼 @Niatruc 这本书买了还没看。元编程还没完全看懂。

18852

define_method只是定义了函数,而不会执行里面的内容。当调用函数时,i的值是20。所以最后结果都打印20。

96

#11楼 @freefishz 嗯,对对对,我确实下意识地以为在define_method时会给j赋以i的值了

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