你怎么能在proc的上下文中评估“local_variables”?在Ruby中

时间:2014-04-24 12:04:51

标签: ruby binding eval ruby-1.8.7

我正在使用ruby 1.8.7(2011-12-28 patchlevel 357)[i686-darwin11.2.0] 我正在玩Kernet :: eval方法。

The binding may be a Binding object or a Proc object.
def eval(string, *binding_filename_lineno)
end

我有以下test.rb

require 'pp'
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}" }
p.call()
pp eval("local_variables", p)

调用ruby test.rb打印

initializing local_var as value
["p"] 

不应该返回["p", "local_var"]

我也看到Ruby 1.9.3 eval只接受一个Binding对象。

1 个答案:

答案 0 :(得分:2)

在proc上定义的

binding方法返回已创建proc的绑定,并且不关心proc内容。

require 'pp'
some_local_var = 'hello'
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}" }
p.call()
pp eval("local_variables", p) #=> ['p', 'some_local_var']

变量local_var仅在proc执行的短时间内存在。你之前执行它的事实并不意味着它仍然在那里(可能是如果GC还没有运行,但你不应该使用它)。一般来说,你应该把它视为无法到达。但是,由于proc可以访问它自己的绑定并且它自带绑定,你可以通过在proc之外声明内部变量来欺骗它:

require 'pp'
local_var = 'hello'
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}" }
pp eval("local_var", p) #=> hello
p.call()
pp eval("local_var", p) #=> value

这可用于创建行为类似于对象的过程:

def get_counter
  i = 0
  proc { p i+=1 }
end

a = get_counter
b = get_counter

5.times { a.call }   #=> 1 2 3 4 5
3.times { b.call }   #=> 1 2 3

eval('i', a)         #=> 5
eval('i', b)         #=> 3

3.times { a.call }   #=> 6 7 8

然而,这不是最佳实践,并且是Ruby程序中大多数内存泄漏的主要原因。

在Ruby 1.8.7中,您可以传递proc而不是绑定,但是eval在内部调用proc上的binding方法。这已在以后删除,因此现在您需要明确地提取它。

pp eval("local_variables", p.binding)

更新:

由于这是红宝石,显然我们可以做任何事情,自然有一种方法可以访问proc内部变量:

require 'pp'
proc_binding = nil;
p = proc { local_var = 'value'; puts "initializing local_var as #{local_var}"; proc_binding = binding }
p.call()
pp eval("local_variables", proc_binding) #=> ["proc_binding", "p", "local_var"]

要小心 - 将内部绑定存储在变量中意味着即使在proc运行之后也会引用local_var,因此GC不会收集它。此外,由于proc引用它并且尚未清理,因此不会在本地重新声明,这几乎是一种破坏生活的快速方法。这些技术在一些非常罕见的情况下可能会有用,但它们通常会导致很多难以调试的问题。