棘手的Ruby范围问题

时间:2016-02-09 20:27:34

标签: ruby

为什么这段Ruby代码不起作用?

cached = {}
def cache(*key)
  cached[key.join] ||= yield
end
cache("key") do "value" end

我收到以下错误:

  

NameError:未定义的局部变量或方法`cached' for main:Object

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

使用实例变量而不是本地:

@cached = {}
def cache(*key)
  @cached[key.join] ||= yield
end

你不能在你的缓存方法中使用本地cached变量,因为局部变量不存在于方法定义之外,所以如果你之前没有在方法体或方法中定义局部变量,Ruby就找不到它。 有关变量可见性的更多信息here

答案 1 :(得分:1)

我会尝试补充@ProgNoob的答案。

如果您来自CC++等语言,其中使用代码块创建范围。

{
   ....
}

该范围内的所有局部变量在外部都不可见,但它们在内部创建的每个范围内都可见,例如:

{
  int a = 3;
  {
     // a is visible inside here
  }
  // a is visible inside here
  ...
}
// a is not visible outside the scope

Ruby不会以同样的方式处理事情。当您创建classmodulemethod时,您正在创建一个全新的范围,而不是从上方范围获取任何内容,就像我们在C中看到的那样。

v0 = 0
class SomeClass # Scope gate
  v1 = 1      
  # only v1 is visible here

  def some_method # Scope gate
    v2 = 2
    # only v2 is visible here        
  end # end of def scope gate
end # end of class scope gate

打破范围大门:

  • 使用Class.new
  • 的类定义
  • Module.new
  • 的模块定义
  • 使用define_method
  • 的方法定义

没有范围门的先前示例

v0 = 0
SomeClass = Class.new do
  v1 = 1
  # v0 and v1 visible here

  define_method(:some_method) do
    v2 = 2
    # v0, v1 and v2 visible here
  end
end
# v0 only visible here