Rubyの変数5種類の解説とサンプル、有効範囲(スコープ)について

logo_long

前回:>> Rubyのコメントの書き方




変数を覚えよう

変数ってなんじゃらホイ?

変数(へんすう)というのは文字や数値を入れる箱です。
忘れないためのメモと思ってもらってもいいです。

変数という箱に5を入れる時のイメージ

変数という箱に5を入れる時のイメージ

文字や数値を箱に入れてどうするの?

後で使うのです。

人間なら一度聞いた数値や文字は覚えていることはできますが、コンピュータは覚えることができません。
変数という箱に入れてあげないと覚えていてくれません。
 

変数は何度でも文字や数値を入れ替えてもいいです。
※入れ替えというか上書きですけど

変数を作ったからと言って、絶対に使わなければいけないということはありません。
※使わないなら作る必要無いんですけど(笑)

変数って何に使うの?

計算式に使ったり、画面表示に使ったり、文字列と文字列を結合して一つの文字列にしたりとかに使います。

例えばこんな感じ。

suuti_a = 1
suuti_b = 2
suuti_c = suuti_a + suuti_b
puts "#{suuti_a} と #{suuti_b} を足すと、答えは #{suuti_c} です。"


kaminoku = "朝起きて 昼寝をしたら 夜寝ます"
simonoku = "いつまで経っても 寝てばかり"
tanka = kaminoku + simonoku
puts "#{tanka}"


hensuu = 1
hensuu = hensuu + 2
hensuu = hensuu * 3
pust hensuu

変数の名前の付け方

ローカル変数

小文字もしくはアンダーバー _ で始まります

グローバル変数

$ で始まります

インスタンス変数

@ で始まります

クラス変数

@@ で始まります

変数の種類の違い

前章で四種類の変数(ローカル変数、グローバル変数、インスタンス変数、クラス変数)を挙げました。
それぞれの違いを見てみましょう。

変数の有効範囲を知ろう!

これらの変数は何が違うかと言うと、有効範囲が違うのです。

変数の有効範囲の事をスコープと言ったりします。

詳しくは後で説明しますが、まずは以下をお読みください。

  • ローカル変数
    ある括りの中だけの範囲で有効。
    「ある括り」とは、メソッドの中だったり、ブロックの中だったりします。
    その括りの外からはその変数を使うことができませんし、その存在を知ることもできません。

    人間の世界に置き換えると、自宅内ルールみたいなもんです。
    自分の家族という狭い範囲の中にあるルールってあると思うんですよね。
    例えば、お風呂に入る順番とか、家族だけ伝わる用語とか、毎週日曜日だけチャンネル権は子どもになるとか。

    他所の家庭からしたら、そんなルールは知らないですよね。

  • グローバル変数
    プログラム全体で使えます。
    ローカル変数のような「ある括り」という概念がなくなったと思ってください。

    でもあまり、グローバル変数は使わないでください。
    ローカル変数で問題ないなら、ローカル変数を使ってください。

    グローバル変数はいつでもどこででも値を変更できてしまいます。
    ということは、変数の値がいつ変わるか把握できなくなってしまいます。

    プログラム量が少ないうちはグローバル変数が変更される箇所やタイミングを全て把握することができます。
    しかし、プログラム量が多くなると、まず把握することは不可能です。
    もちろん不可能ではありませんし表にまとめるとかしてもいいですけど、分かりにくい/管理しにくい事は間違いありません。

    よっぽどの理由が無いなら、グローバル変数を使う使うのは控えましょう。
    使うにしても、使う数は少なく・更新する場所も少なくしましょう。

  • インスタンス変数
    クラスやインスタンスをまだ理解していない場合、今は読まなくていいです。

    クラスをインスタンス化して使いますが、インスタンス毎に持つ変数です。
    1つのクラスからインスタンスは何個でも作れますが、インスタンス変数は別々に独立したものとなります。

    例えば、@hoge というインスタンス変数がそのクラスにあったとしましょう。
    インスタンスAが持つ @hoge と、インスタンスBが持つ @hoge とは別々のものです。
    もちろんインスタンスAがインスタンスBが持つ @hoge を読み書きすることはできません。
    その反対も同じです。

  • クラス変数
    クラスやインスタンスをまだ理解していない場合、今は読まなくていいです。

    インスタンス変数が理解できれば、クラス変数のスコープが想像できるんじゃないでしょうか?

    全てのインスタンスで共有する変数がクラス変数です。
    インスタンスAが見ている @@hoge と、インスタンスBが見ている @@hoge は同じものです。

    「遠く離れて住む二人が同じ月を見ている」という感じですかね。(なかなかオシャレな表現ですよね 笑)

  • クラスインスタンス変数
    クラスやインスタンスをまだ理解していない場合、今は読まなくていいです。
    かなりややこしいので、よく分からなければスルー推奨。

    見た目はインスタンス変数、中身はインスタンスでは使えない変数www

    しかし、インスタンスして読み書きすることはできない。
    クラスメソッドでのみ読み書き可能。

    但し、このクラスを継承した子クラスは読み書きできない。

 

基本的にはローカル変数だけを使うようにすればいいと思います。

 

なぜって?

ローカル変数が一番分かり易いから。

 

使う範囲を限定している方が、

  1. どんなタイミングで変更されて、
  2. どんなタイミングで参照されるか

が分かり易いのです。

 

じゃぁなんで分かり易くする必要があるの?

それは、不具合の修正や仕様変更をし易くするためです。

 

プログラムは一回書いて終了ではありません。

ブログとか作文ですら何度も読み返して書き直してってしますよね?
プログラムも書いては見直し/書き直し、間違いを修正します(間違ってる箇所を探すのが結構大変なのよ)。

正しく書けていても、やっぱり動きを変更しようと思って修正することもあります。

サンプル

細かい説明はあえて書いていません。
説明読むより、結果結果とプログラムを突き合わせて読む方が早いし分かりやすいですよ。

ローカル変数の場合

コード

# localhensuuとhoge に注目

#********************************************
# メソッド1
#********************************************
def HogeMethod()
  localhensuu = 300          # ★
  hoge = "B"                 # ★
  
  puts "メソッド1-1:#{localhensuu}:(#{hoge})"

  arrayhensuu = ["か", "き", "く"]
  arrayhensuu.each { |hoge|  # ★
    localhensuu = 400        # ★
    puts "メソッド1-2:#{localhensuu}:(#{hoge})"
  }

  # HogeMethod2()を呼び出す
  HogeMethod2()

  puts "メソッド1-3:#{localhensuu}:(#{hoge})"
end

#********************************************
# メソッド2
#********************************************
def HogeMethod2()
  localhensuu = 500          # ★
  hoge = "C"                 # ★
  
  puts "メソッド2-1:#{localhensuu}:(#{hoge})"

  arrayhensuu = ["さ", "し", "す"]
  arrayhensuu.each { |hoge|  # ★
    localhensuu = 600        # ★
    puts "メソッド2-2:#{localhensuu}:(#{hoge})"
  }

  puts "メソッド2-3:#{localhensuu}:(#{hoge})"
end


#********************************************
# プログラムはここからスタート
#********************************************
localhensuu = 100            # ★
hoge = "A"                   # ★

puts "メイン-1:#{localhensuu}:(#{hoge})"

arrayhensuu = ["あ", "い", "う"]
arrayhensuu.each { |hoge|    # ★
  localhensuu = 200          # ★
  puts "メイン-2:#{localhensuu}:(#{hoge})"
}

# HogeMethod()を呼び出す
HogeMethod()

puts "メイン-3:#{localhensuu}:(#{hoge})"

結果

C:/temp/local.rb:13: warning: shadowing outer local variable - hoge
C:/temp/local.rb:34: warning: shadowing outer local variable - hoge
C:/temp/local.rb:52: warning: shadowing outer local variable - hoge
メイン-1:100:(A)
メイン-2:200:(あ)
メイン-2:200:(い)
メイン-2:200:(う)
メソッド1-1:300:(B)
メソッド1-2:400:(か)
メソッド1-2:400:(き)
メソッド1-2:400:(く)
メソッド2-1:500:(C)
メソッド2-2:600:(さ)
メソッド2-2:600:(し)
メソッド2-2:600:(す)
メソッド2-3:600:(C)
メソッド1-3:400:(B)
メイン-3:200:(A)

※warning出ていますが、気にしないでください。
※気になる人は調べてみてください。

グローバル変数の場合

コード

# $localhensuuと$hoge に注目

#********************************************
# メソッド1
#********************************************
def HogeMethod()
  $hoge = "B"                # ★
  
  puts "メソッド1-1:#{$localhensuu}:(#{$hoge})"

  arrayhensuu = ["か", "き", "く"]
  arrayhensuu.each { |hoge|  # ★
    puts "メソッド1-2:#{$localhensuu}:(#{$hoge})"
  }

  # HogeMethod2()を呼び出す
  HogeMethod2()

  puts "メソッド1-3:#{$localhensuu}:(#{$hoge})"
end

#********************************************
# メソッド2
#********************************************
def HogeMethod2()
  $hoge = "C"                # ★
  
  puts "メソッド2-1:#{$localhensuu}:(#{$hoge})"

  arrayhensuu = ["さ", "し", "す"]
  arrayhensuu.each { |hoge|  # ★
    puts "メソッド2-2:#{$localhensuu}:(#{$hoge})"
  }

  puts "メソッド2-3:#{$localhensuu}:(#{$hoge})"

  $localhensuu = 200         # ★
end


#********************************************
# プログラムはここからスタート
#********************************************
$localhensuu = 100           # ★
$hoge = "A"                  # ★

puts "メイン-1:#{$localhensuu}:(#{$hoge})"

arrayhensuu = ["あ", "い", "う"]
arrayhensuu.each { |hoge|    # ★
  puts "メイン-2:#{$localhensuu}:(#{$hoge})"
}

# HogeMethod()を呼び出す
HogeMethod()

puts "メイン-3:#{$localhensuu}:(#{$hoge})"

結果

メイン-1:100:(A)
メイン-2:100:(A)
メイン-2:100:(A)
メイン-2:100:(A)
メソッド1-1:100:(B)
メソッド1-2:100:(B)
メソッド1-2:100:(B)
メソッド1-2:100:(B)
メソッド2-1:100:(C)
メソッド2-2:100:(C)
メソッド2-2:100:(C)
メソッド2-2:100:(C)
メソッド2-3:100:(C)
メソッド1-3:200:(C)
メイン-3:200:(C)

インスタンス変数

コード

class Hoge

  def initialize
    @instancehensuu = 0
  end
  
  def plus
    @instancehensuu += 1
  end

  def ans
    return @instancehensuu
  end
end


hoge1 = Hoge.new
hoge1.plus
puts "hoge1の結果、#{hoge1.ans}"

hoge2 = Hoge.new
hoge2.plus
hoge2.plus
hoge2.ans
puts "hoge2の結果、#{hoge2.ans}"

hoge3 = Hoge.new
hoge3.plus
hoge3.plus
hoge3.plus
hoge3.ans
puts "hoge3の結果、#{hoge3.ans}"

結果

hoge1の結果、1
hoge2の結果、2
hoge3の結果、3

クラス変数

コード

class Hoge

  @@instancehensuu = 0
  
  def plus
    @@instancehensuu += 1
  end

  def ans
    return @@instancehensuu
  end
end


hoge1 = Hoge.new
hoge1.plus
puts "hoge1の結果、#{hoge1.ans}"

hoge2 = Hoge.new
hoge2.plus
hoge2.plus
hoge2.ans
puts "hoge2の結果、#{hoge2.ans}"

hoge3 = Hoge.new
hoge3.plus
hoge3.plus
hoge3.plus
hoge3.ans
puts "hoge3の結果、#{hoge3.ans}"

結果

hoge1の結果、1
hoge2の結果、3
hoge3の結果、6

クラスインスタンス変数

コード

class Hoge

  @classinstancehensuu = 123

  def self.plus
    @classinstancehensuu += 1
  end

  def self.ans
    return @classinstancehensuu
  end
  
end

class Hoge2 < Hoge

end

puts "Hogeの結果、#{Hoge.ans}"
Hoge.plus
puts "Hogeの結果、#{Hoge.ans}"

puts "Hoge2の結果、#{Hoge2.ans}"
Hoge2.plus
puts "Hoge2の結果、#{Hoge2.ans}"

結果

Hogeの結果、123
Hogeの結果、124
C:/temp/classinstance.rb:10: warning:
instance variable @classinstancehensuu not initialized
Hoge2の結果、
C:/temp/classinstance.rb:6: warning: i
nstance variable @classinstancehensuu not initialized
C:/temp/classinstance.rb:6:in `plus':
undefined method `+' for nil:NilClass (NoMethodError)
        from C:/temp/classinstance.rb:
24:in `
'

※エラーが出てますね
※なぜエラーが出ているか考えてみてください

 




 

変数って絶対に使わないといけないの?

そんなことはありません。
変数を使わず直接値を書いてもいいですけど、以下の理由により変数にすると便利なのです。

  • 何度も同じ値を書くのが面倒
  • その値が変更になった場合、何箇所も書き直す必要があるけど、変数にしておけば書き直す必要が無い(変数に値をセットする箇所だけ修正すれば良い)

いや!変数なんて不要だ!
と思う場合は一切変数を使わずにプログラミングしてみましょう。
きっと「めんどくせーーー!」ってなると思います。
そう思ってから変数を使うと、変数の便利さが分かるかと思います。

できれば覚えて欲しい:変数の型

変数には型があります。
 

変数の『型』ってなんやねん???

型というのは、変数の形です。
 

と言っても分かりにくいですね。

データは「モノ」、変数は「入れ物」です。

これを人間の世界に置き換えてみましょう。

人間の世界のモノと入れ物の関係
モノ:靴
入れ物:シューズ・バッグ

モノ:ギター
入れ物:ギターケース

モノ:定期券
入れ物:定期券入れ

モノ:名刺
入れ物:名刺入れ

モノ:刀
入れ物:鞘

↑こんな感じでモノと入れ物は対になってますよね。

モノの形に合った入れ物に、モノを入れますよね。

入れ物から考えたとしても、ギターケースに入っているモノはギターと考えますよね。
シューズ・バッグに入っているモノは靴と思いますよね。

それに、それぞれに合ったものじゃないと入らないですよね?
シューズバッグにゴルフクラブは入りませんよね。
※「ギターケースに靴を入れる事できるぞーーー!」「オレは定期券入れに名刺入れてるぞーーー!」というツッコミは却下します!(笑)

モノを入れ物に入れると言っても、何も考えずに入れていいワケではありません。
モノの形と、入れ物の形が合っている必要があります。

 

ってことで、数値は数値型に、文字は文字型に、オブジェクトはオブジェクト型に、アドレスはアドレス型に入れます。

  ↓

  ↓

  ↓

  ↓

・・・みたいなことを書いておいてなんなんですが、Rubyには『型』という概念がありません。
※正確に言うと型が無いワケではありません。動的に型が決まる(or変更される)だけです。プログラマが気にしなくてもいいだけです。

 

じゃぁ、なんで型を覚えろなんて書いたんだよーーー!!

 

変数にどんな型の値が入っているのか、意識する必要があるからです。

 

Ruby以外の「変数の型にうるさい言語」を使う場合:
数値型の変数に入っているのは数値のみ、文字型の変数に入っているのは文字のみと考えればいい
「変数の型にうるさくない言語」(Ruby等)を使う場合:
今から使う変数を使う場合、数値が入っているのか文字が入っているのか分からない
計算式に使う変数の中身が数値とは限らず、abc とか あいう とかが入っている可能性がある

ってなわけで、前者の場合は使う変数さえ間違わなければ型で問題になることは無いでしょう。
しかし、後者の場合は使う変数が正しくても入っている内容がおかしい場合には思ったような動きになりません。
※「数値 + 数値」を期待しているのに、「数値 + 文字」になって思った結果を得られないとか。

Rubyは変数の型にうるさくないので使いやすい面があるのは確かです。
文字を入れることもできるし数値を入れることもできる。
これが便利な時があるのは確かです。

しかし変数に入る値をプログラムを書く人がちゃんと管理/把握しておく必要があるので、よく注意しましょう。

変数の型がおかしい場合のサンプル

コード

a = 1
b = 2
c = a + b
puts "答えは、#{c}"

puts "---------------------"

a = 1
b = "w"
c = a + b     # bに数値が入っていると勘違いして計算してみた
puts "答えは、#{c}"

結果

答えは、3
---------------------
C:/temp/funny.rb:10:in `+': String can
't be coerced into Fixnum (TypeError)
        from C:/temp/funny.rb:10:in `<
main>'

※エラーが出てますね
※なぜエラーが出ているか考えてみてください

疑似変数

擬似変数と呼ばれるものがあります。
変数ではないのですが、変数に見えるので疑似変数と呼ばれています。

  • nil
  • true
  • false
  • self

別に「疑似変数」という言葉は覚えなくていいです。
いや、むしろ覚えなくていいです(笑)

混乱の元になるかも知れませんので。

って言うか、私もこの記事書く時に「疑似変数」って言葉知りましたwww

関連記事

以下の記事でも変数について書いています。
良かったらご覧ください。

>> [Atomで覚えるC言語入門 – 初歩の初歩 – ] No.02 変数と式


前回:>> Rubyのコメントの書き方


 

 

おすすめ書籍

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

ABOUTこの記事をかいた人

Windows/Mac/Linuxを使う現役システムエンジニア&プログラマ。オープン系・組み込み系・制御系・Webシステム系と幅広い案件に携わる。C言語やC#やJava等数多くのコンパイラ言語を経験したが、少し飽きてきたので、最近はRubyやPython、WordPressなどのWeb系を修得中。初心者向けのプログラミング教室も運営中。オンライン・対面・出張等でプログラミングをレッスンします。