【问题标题】:how can you eval "local_variables" in context of a proc? in Ruby您如何在 proc 的上下文中评估“local_variables”?在红宝石
【发布时间】:2014-04-24 12:04:51
【问题描述】:

我正在使用 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 对象。

【问题讨论】:

    标签: ruby binding eval ruby-1.8.7


    【解决方案1】:

    在 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
    

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

    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)
    

    更新:

    由于这是 ruby​​,显然我们可以做任何事情,自然有一种方法可以访问 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 正在引用它并且尚未清理,因此不会在本地重新声明它,这几乎是毁掉你生活的快速方法。这些技术在极少数情况下可能很有用,但它们通常会导致很多极其难以调试的问题。

    【讨论】:

    • 您的“创建行为类似于对象的过程”的示例可能就是所谓的闭包。
    • 另外我不认为在最后一个例子中它会阻止你重新声明 local_var。至少它可以在我的机器上运行。
    • @Usman - 我的意思是,如果你再次调用 proc,local_var 甚至在它被分配之前就被声明了。在提供的示例中,它没有任何区别,但如果您使用 if 检查它的值,它就会有任何区别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多