Ruby Language
可変スコープと可視性
サーチ…
構文
- $ global_variable
- @@ class_variable
- @インスタンス変数
- local_variable
備考
クラス変数はクラス階層で共有されます。これは驚くべき振る舞いにつながります。
class A
@@variable = :x
def self.variable
@@variable
end
end
class B < A
@@variable = :y
end
A.variable # :y
クラスはオブジェクトなので、インスタンス変数を使用して各クラスに固有の状態を提供することができます。
class A
@variable = :x
def self.variable
@variable
end
end
class B < A
@variable = :y
end
A.variable # :x
ローカル変数
ローカル変数(他の変数クラスと違って)には接頭辞がありません
local_variable = "local"
p local_variable
# => local
そのスコープは宣言された場所に依存し、 "宣言コンテナ"スコープの外では使用できません。たとえば、ローカル変数がメソッド内で宣言されている場合は、そのメソッド内でのみ使用できます。
def some_method
method_scope_var = "hi there"
p method_scope_var
end
some_method
# hi there
# => hi there
method_scope_var
# NameError: undefined local variable or method `method_scope_var'
もちろん、ローカル変数はメソッドに限定されず、 do ... end
ブロックの中に変数を宣言するか、中括弧{}
で囲むとすぐに、それはローカルとスコープになりますそれが宣言されたブロック
2.times do |n|
local_var = n + 1
p local_var
end
# 1
# 2
# => 2
local_var
# NameError: undefined local variable or method `local_var'
しかし、 if
またはcase
ブロックで宣言されたローカル変数は、親スコープで使用できます。
if true
usable = "yay"
end
p usable
# yay
# => "yay"
ローカル変数は宣言ブロックの外では使用できませんが、ブロックに渡されます。
my_variable = "foo"
my_variable.split("").each_with_index do |char, i|
puts "The character in string '#{my_variable}' at index #{i} is #{char}"
end
# The character in string 'foo' at index 0 is f
# The character in string 'foo' at index 1 is o
# The character in string 'foo' at index 2 is o
# => ["f", "o", "o"]
しかし、メソッド/クラス/モジュールの定義ではない
my_variable = "foo"
def some_method
puts "you can't use the local variable in here, see? #{my_variable}"
end
some_method
# NameError: undefined local variable or method `my_variable'
ブロック引数に使用される変数は、(もちろん)ブロックに対してローカルですが、上書きすることなく、以前に定義された変数を覆い隠してしまいます。
overshadowed = "sunlight"
["darkness"].each do |overshadowed|
p overshadowed
end
# darkness
# => ["darkness"]
p overshadowed
# "sunlight"
# => "sunlight"
クラス変数
クラス変数はクラスワイドスコープを持ち、クラス内のどこにでも宣言することができます。変数は、接頭辞@@
ときにクラス変数と見なされます
class Dinosaur
@@classification = "Like a Reptile, but like a bird"
def self.classification
@@classification
end
def classification
@@classification
end
end
dino = Dinosaur.new
dino.classification
# => "Like a Reptile, but like a bird"
Dinosaur.classification
# => "Like a Reptile, but like a bird"
クラス変数は関連クラス間で共有され、子クラスから上書きできます
class TRex < Dinosaur
@@classification = "Big teeth bird!"
end
TRex.classification
# => "Big teeth bird!"
Dinosaur.classification
# => "Big teeth bird!"
この動作はほとんどの場合不要であり、クラスレベルのインスタンス変数を使用することで回避できます。
モジュール内で定義されたクラス変数は、そのクラスを含むクラス変数を上書きしません。
module SomethingStrange
@@classification = "Something Strange"
end
class DuckDinosaur < Dinosaur
include SomethingStrange
end
DuckDinosaur.class_variables
# => [:@@classification]
SomethingStrange.class_variables
# => [:@@classification]
DuckDinosaur.classification
# => "Big teeth bird!"
グローバル変数
グローバル変数はグローバルなスコープを持つため、どこでも使用できます。スコープは、定義されている場所に依存しません。 $
記号が前に付いた変数は、グローバル変数と見なされます。
$i_am_global = "omg"
class Dinosaur
def instance_method
p "global vars can be used everywhere. See? #{$i_am_global}, #{$another_global_var}"
end
def self.class_method
$another_global_var = "srsly?"
p "global vars can be used everywhere. See? #{$i_am_global}"
end
end
Dinosaur.class_method
# "global vars can be used everywhere. See? omg"
# => "global vars can be used everywhere. See? omg"
dinosaur = Dinosaur.new
dinosaur.instance_method
# "global vars can be used everywhere. See? omg, srsly?"
# => "global vars can be used everywhere. See? omg, srsly?"
グローバル変数はどこにでも定義でき、どこでも見ることができるので、 "未定義"グローバル変数を呼び出すと、エラーを発生させる代わりにnilが返されます。
p $undefined_var
# nil
# => nil
グローバル変数は使いやすいですが、定数の使用を強くお勧めします。
インスタンス変数
インスタンス変数にはオブジェクトのスコープがあり、オブジェクトのどこにでも宣言できますが、クラスレベルで宣言されたインスタンス変数はクラスオブジェクトでのみ表示されます。 @
接頭辞が付いた変数はインスタンス変数と見なされます。インスタンス変数は、オブジェクトの属性を設定および取得するために使用され、定義されていない場合はnilを返します。
class Dinosaur
@base_sound = "rawrr"
def initialize(sound = nil)
@sound = sound || self.class.base_sound
end
def speak
@sound
end
def try_to_speak
@base_sound
end
def count_and_store_sound_length
@sound.chars.each_with_index do |char, i|
@sound_length = i + 1
p "#{char}: #{sound_length}"
end
end
def sound_length
@sound_length
end
def self.base_sound
@base_sound
end
end
dino_1 = Dinosaur.new
dino_2 = Dinosaur.new "grrr"
Dinosaur.base_sound
# => "rawrr"
dino_2.speak
# => "grrr"
クラスレベルで宣言されたインスタンス変数は、オブジェクトレベルではアクセスできません。
dino_1.try_to_speak
# => nil
しかし、新しいメソッドにサウンドが渡されないときにインスタンス変数@base_sound
を使用してサウンドをインスタンス化しました。
dino_1.speak
# => "rawwr"
インスタンス変数は、たとえブロック内であっても、オブジェクト内のどこにでも宣言することができます。
dino_1.count_and_store_sound_length
# "r: 1"
# "a: 2"
# "w: 3"
# "r: 4"
# "r: 5"
# => ["r", "a", "w", "r", "r"]
dino_1.sound_length
# => 5
インスタンス変数は、同じクラスのインスタンス間で共有されません
dino_2.sound_length
# => nil
これは、クラスがRubyのオブジェクトでもあるため、子クラスによって上書きされないクラスレベルの変数を作成するために使用できます。
class DuckDuckDinosaur < Dinosaur
@base_sound = "quack quack"
end
duck_dino = DuckDuckDinosaur.new
duck_dino.speak
# => "quack quack"
DuckDuckDinosaur.base_sound
# => "quack quack"
Dinosaur.base_sound
# => "rawrr"