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

Niatruc · December 30, 2016 · Last by Niatruc replied at December 31, 2016 · 1925 hits

刚刚偶然发现 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 解释器的伙伴解释解释呗~如果我想达到我要的结果,有没有好的写法?

不懂怎样才会有

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 Floor has deleted

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

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

看完这个你就懂了。

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

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

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

#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

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

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

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

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

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

You need to Sign in before reply, if you don't have an account, please Sign up first.