LoginSignup
2
0

More than 3 years have passed since last update.

(初学者向け)Rubyの変数について網羅的に確認しよう

Last updated at Posted at 2020-05-16

Rubyの変数についてごっちゃになっていたので網羅的にまとめました。

記事の対象

Rubyを触ったことがある
クラスや継承、インスタンスについてなんとなくでもいいので理解している
Rubyの変数についてなんとなくわかるが曖昧な方

この記事で解説すること

各変数の各スコープについて
  • ローカル変数
  • ブロック変数
  • インスタンス変数
  • クラスインスタンス変数
  • クラス変数
  • グローバル変数

用語

スコープ・・・変数が参照できる範囲のこと(変数の有効範囲)
スーパークラス・・・継承元のクラス(親クラス)
サブクラス・・・継承先のクラス(子クラス)
ブロック・・・ doとendまたは{ }で囲まれた範囲のこと

検証方法

クラス内・クラス外・インスタンスメソッド内・クラスメソッド内、継承先でpメソッドを用いて各変数の返り値を見ていきます。
クラス等の説明は本記事では省きます。(機会があれば別で書こうと思います。)
また、同じような処理をあえてまとめずに繰り返して書いております。
これはひとかたまりの情報量を少なくすることでコード一つひとつの動きがわかりやすいようにあえてしております。
コードもひとかたまりが少ないので動きがわからなかったらコピペして自分で値を変えたりして動かしてみると理解が深まるかもしれません。

もし見づらい、わかりづらいと感じましたらコメントで教えていただけると嬉しいです。

Rubyの変数

ローカル変数

変数といえばイメージするのはこれ。定義場所によってスコープが異なる。

  • 書き方 変数名(小文字) = 値

クラスの外で定義しているのでclassからendまでの間(クラス内)では参照できない。

クラス外で定義、クラス内から呼び出し
x = 10
p x #=> 10

class Sample
  p x #=> undefined local variable or method `x' for Sample:Class
end

もちろんクラス内で定義すればクラス内がスコープになる(同じ変数名でもスコープが違うので上書きはされない、ブロック内でも同様)

クラス内外両方で定義
x = 10
p x #=> 10

class Sample
  x = 20
  p x #=> 20
end

p x #=> 10

defからendまでの間(インスタンスメソッド内)でも同様に参照できない。

インスタンスメソッド内での呼び出し
x = 10
p x #=> 10

class Sample
  def hoge #インスタンスメソッドの定義
    p x #=> undefined local variable or method `x' for #<Sample:0x00007fd44313c7b0>
  end
end

sample = Sample.new #インスタンスの生成
sample.hoge #インスタンスメソッドの呼び出し

クラスメソッド内でも同様

クラスメソッド内での呼び出し
x = 10
p x #=> 10

class Sample
  def self.hoge #クラスメソッドの定義
    p x #=> undefined local variable or method `x' for Sample:Class
  end
end

Sample.hoge #クラスメソッドの呼び出し

ブロック変数

スコープがブロック内のみの変数

  • 書き方 do |変数名| endもしくは {|変数名|}

doからendの間以外では参照できない。

ブロック外からの呼び出し
numbers = [10, 20, 30]
numbers.each do |num| #numというブロック変数を定義
  p num
end
#=> 10
#=> 20
#=> 30

p num #=> undefined local variable or method `num' for main:Object

インスタンス変数

インスタンスメソッド内と継承先のインスタンスメソッド内で使える

  • 書き方 @変数名 = 値

インスタンスメソッド内から参照できます。

インスタンスメソッド内での呼び出し
class Sample
  def hoge #インスタンスメソッドの定義
    @x = 10
    p @x
  end

  def fuga #インスタンスメソッドの定義
    p @x
  end
end
sample = Sample.new
sample.hoge #=> 10
sample.fuga #=> 10

クラスメソッド内からは参照できません。ただしインスタンス変数はエラーにならずnilが返ります!

クラスメソッド内での呼び出し
class Sample
  def self.hoge #クラスメソッドの定義
    p @x
  end

  def fuga #インスタンスメソッドの定義
    @x = 10
    p @x
  end
end
Sample.hoge #=> nil
sample = Sample.new
sample.fuga #=> 10

クラス内でもクラス外でも参照できません。同様にnilが返ります。

クラス内での呼び出し
class Sample

  p @x #=> nil

  def hoge #インスタンスメソッドの定義
    @x = 10
    p @x
  end

  p @x #=> nil

end
sample = Sample.new
sample.hoge #=> 10
クラス外での呼び出し
class Sample
  def hoge #インスタンスメソッドの定義
    @x = 10
    p @x
  end
end
sample = Sample.new
sample.hoge #=> 10
p @x #=> nil

継承先のインスタンスメソッド内からは参照できます。

継承先インスタンスメソッドからの呼び出し
class Sample
  def hoge
    @x = 10 #これはインスタンス変数
    p @x
  end
end

class SubSample < Sample #クラスの継承
  def fuga
    p @x
  end
end

sample = Sample.new
sample.hoge #=> 10
sub_sample = SubSample.new
sub_sample.hoge #Sampleクラスを継承しているためインスタンスメソッドが使える #=> 10
sub_sample.fuga #fugaメソッドからでもhogeメソッド内のインスタンス変数が参照できている #=> 10

あくまでもインスタンス変数なのでサブクラス直下では参照できません。(上記のようにサブクラス内のインスタンスメソッドからは参照できる)

継承先クラス内での呼び出し
class Sample
  def hoge
    @x = 10 #これはインスタンス変数
    p @x
  end
end

class SubSample < Sample #クラスの継承
  p @x #=> nil
end

クラスインスタンス変数

  • 書き方 @変数名 = 値

まさかのインスタンス変数と同じ書き方でものすごく紛らわしい(泣)
ではインスタンス変数と何が違うのか
@scivolaさんからコメントいただいたので修正いたします。
クラスもオブジェクトのためインスタンス変数を持てます。そのためclass直下で定義したインスタンス変数はhogeメソッド内で出力しようとしているインスタンス変数とは別物になります。
これはobject_idを確認することでわかります。
クラスが持つインスタンス変数を特にクラスインスタンス変数と呼びます。
(@scivolaさんありがとうございました!)

インスタンスメソッド内での呼び出し
class Sample
  @class_instance_var = "class_instance_var" #これはクラスのインスタンス変数
  p @class_instance_var.object_id #=> 70139563271980
  def hoge #インスタンスメソッドの定義
    p @class_instance_var
    p @class_instance_var.object_id #=> 8
  end
end

sample = Sample.new
sample.hoge #=> nil

変数名はなんでも良いのですが、ここでは@class_instance_varと名前を付けました。
クラス直下で@を付けて定義するとクラスのインスタンス変数(クラスインスタンス変数)になります。

返り値を見てみると@class_instance_varはnilになってしまっていることがわかります。

クラス内での呼び出し
class Sample
  @class_instance_var = 10 #これはクラスのインスタンス変数なので
  p @class_instance_var    # 参照できる #=> 10
end
クラスメソッド内での呼び出し
class Sample
  @class_instance_var = 10
  def self.hoge
    p @class_instance_var
  end
end

Sample.hoge #これもOK #=> 10

継承先ではオブジェクトが違うためnilになります。

継承先での呼び出し
class Sample
  @class_instance_var = "class_instance_var" #これはクラスインスタンス変数
  p @class_instance_var #=> "class_instance_var"
  p @class_instance_var.object_id #=> 70266520708580
end

class SubSample < Sample #クラスの継承
  p @class_instance_var #=> nil
  p @class_instance_var.object_id #=> 8
end

これは@class_instance_varはSampleクラスのインスタンス変数(クラスインスタンス変数)なのでSubSampleクラスでは参照できないことを表しています。(Sampleクラスの@class_instance_varとSubSampleクラスの@class_instance_varはオブジェクトが違うため参照できない)

クラス変数

  • 書き方 @@変数名 = 値

また変なのが出てきました(笑)
変数の前に@を2つ付けます。小さなプログラムではあまり必要とされませんが、ライブラリの設定情報等で使われることがあります。
クラス内、クラスメソッド、インスタンスメソッド、継承先のクラスから参照することができます。

クラス内での呼び出し
class Sample
  @@x = 10
  p @@x #=> 10
end
クラスメソッド内での呼び出し
class Sample
  @@x = 10
  def self.hoge
    p @@x
  end
end

Sample.hoge #=> 10
インスタンスメソッド内での呼び出し
class Sample
  @@x = 10
  def hoge
    p @@x
  end
end

sample = Sample.new
sample.hoge #=> 10
継承先クラスから呼び出し
class Sample
  @@x = 10
end

class SubSample < Sample
  p @@x #=> 10
end
継承先クラスメソッドからの呼び出し
class Sample
  @@x = 10
end

class SubSample < Sample
  def self.hoge
    p @@x
  end
end

SubSample.hoge #=> 10
継承先インスタンスメソッドからの呼び出し
class Sample
  @@x = 10
end

class SubSample < Sample
  def hoge
    p @@x
  end
end

subsample = SubSample.new
subsample.hoge #=> 10

以下はエラーになる

クラス外での呼び出し
class Sample
  @@x = 10
end

p @@x #=> uninitialized class variable @@x in Object

グローバル変数

どこからでも参照できる変数、スコープが広いものはあまり良くないので基本的に使わないと思う。

  • 書き方 $変数名 = 値
クラス内での呼び出し
$x = 10
p $x #=> 10

class Hoge
  p $x #=>10
end
インスタンスメソッド内での呼び出し
$x = 10

class Sample
  def hoge
    p $x
  end
end

sample = Sample.new
sample.hoge #=> 10
クラスメソッド内での呼び出し
$x = 10

class Sample
  def self.hoge
    p $x
  end
end

Sample.hoge #=> 10

以下の場合は上から順次実行されていくので最後の$xは20に書き変わってしまいますね。

クラス外での呼び出し
$x = 10
p $x #=> 10
class Sample
  $x = 20
end

p $x #=> 20

組込変数・特殊変数

$で始まるrubyが持っている変数
本記事では割愛
気になる方は以下を参照してください。
https://gist.github.com/kwatch/2814940

最後に

実際のところローカル変数とインスタンス変数以外はあまり使う機会がないかもしれません。
ただ、こういった動きは理解しておきたいですね!

またスコープの大きさとしては

グローバル変数 > クラス変数 > クラスインスタンス変数 ≒ インスタンス変数 > ローカル変数 >ブロック変数

といったところでしょうか。(もちろん定義場所によって異なります!)
スコープが大きい変数を使うと知らぬ間に上書きされる可能性がありバグの発生につながるので、スコープはなるべく小さくするのが良いとされてます。
眠い頭を動かして書いたので間違っているところがあれば是非教えていただけると助かります!

2
0
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0