Ruby local variable scope

2019-09-12 18:57发布

问题:

I'm struggling with variable scope in ruby. I was under the impression that local variables were accessible by methods below them. Looking at the following code however I'm getting an undefined variable error.

a = Array.new

def testing()
  a.push("test")
end

testing

Using global variables it works just fine, how can I avoid using global variables?

回答1:

There isn't much to say here except that local variables in Ruby are only accessible in the scope in which they are defined and any blocks (closures) defined in that scope that capture them. Since in Ruby, unlike some other dynamic languages, method are not closures, they do not capture local variables.

If you tried, say, this:

irb(main):001:0> a = 3
=> 3
irb(main):002:0> define_method(:testing) do
irb(main):003:1*   puts a
irb(main):004:1> end
=> :testing
irb(main):005:0> testing
3

It works, since the code is in a block instead of a method.



回答2:

Defining a method in the top-level can be quite confusing. Let's wrap your code in a class instead:

class Foo
  a = []

  def testing
    a << 'test'
  end
end

(I've shortened Array.new to [] and a.push(...) to a << ...)

Foo#testing can be called via:

foo = Foo.new
foo.testing
#=> undefined local variable or method `a'

Apparently, this doesn't work. The first a is a local variable in the scope of the class body, whereas the second a is a local variable within an instance method.

Moving the variable initialization out of the class body into the initialize method doesn't work either, because local variables are not shared across methods:

class Foo
  def initialize
    a = []         # <- one 'a'
  end

  def testing
    a << 'test'    # <- another 'a'
  end
end

To get this working, you have to use an instance variable:

class Foo
  def initialize
    @a = []
  end

  def testing
    @a << 'test'
  end
end

foo = Foo.new
foo.testing
#=> ["test"]

foo.testing
#=> ["test", "test"]


回答3:

You could use instance variables. Any variable whose name begins with @ is an instance variable and is available anywhere in the class or method in which it is defined. For example, the variable @A defined within class B will be available to any methods in B.



回答4:

2.3.3 :007 > def testing()
2.3.3 :008?>   [].push("test")
2.3.3 :009?>   end
 => :testing
2.3.3 :010 > testing
 => ["test"]

You can't let local variables accessible by methods below them , you can use block like the answer by @Linuxios, or use the way that it easy work.