あれ、変数が使えない?
プラクティスでコードを書いているときに変数が定義されてないよーというエラーが出てしまうことがありました。
例えば、こんなコードがエラーになります。
%w[月 火 水 木 金 土 日].each do |day| week = {} holiday = %w[土 日] if holiday.include?(day) week[day] = '休日' else week[day] = '平日' end end puts week
エラーメッセージがでる
`<main>': undefined local variable or method `week' for main:Object (NameError) puts week ^^^^
これはweekをブロックの外に出せば正しく動きます。
week = {} %w[月 火 水 木 金 土 日].each do |day| holiday = %w[土 日] if holiday.include?(day) week[day] = '休日' else week[day] = '平日' end end puts week
正常に動きました
{"月"=>"平日", "火"=>"平日", "水"=>"平日", "木"=>"平日", "金"=>"平日", "土"=>"休日", "日"=>"休日"}
原因はブロック内で変数を定義していたのでブロック外では使えないということでした。
情報を整理するために、一度変数とそのスコープをじっくり検証してみたいと思います。
変数とスコープ検証
ローカル変数
local_variable = 1
基本の変数です。
スコープは宣言したブロック、メソッド定義、クラス/モジュール定義の中。
インスタンス変数
@instance_variable = 1
クラス定義の中で宣言します。
スコープはnewしたインスタンス内なので別のメソッドからも呼べます。
class Practice def instance_variable @instance_variable end def instance_variable=(value) @instance_variable = value end end practice =Practice.new practice.instance_variable = 1 puts practice.instance_variable #=> 1
初期値を与えようとしてこう書くとnilが返ってきます。
class Practice @instance_variable = 1 def instance_variable @instance_variable end end practice =Practice.new p practice.instance_variable #=> nil
これはクラス定義の最中に定義されたインスタンス変数はクラスが持つインスタンス変数になり、newされたインスタンスはインスタンス自身が持つインスタンス変数になるから。要はそれぞれ別のオブジェクトという事です。
初期値を与えたいのであればinitializeの中で指定することができます。
class Practice def initialize @instance_variable = 1 end def instance_variable @instance_variable end end practice =Practice.new p practice.instance_variable #=> 1
クラス変数
クラス変数はクラス定義の中で定義されます。
スコープはクラス内とインスタンス内、さらにサブクラスからもアクセスできます。
@@class_variable = 1
class Practice @@class_variable = 2 def class_variable @@class_variable end end practice =Practice.new p practice.class_variable #=> 2
クラスメソッドを使えばクラスから直接参照もできます。
class Practice @@class_variable = 2 def self.class_variable @@class_variable end end p Practice.class_variable #=> 2
クラス変数は別のインスタンスであっても同じオブジェクトを参照します。 クラス自身も同じオブジェクトを参照します。
class Practice def class_variable @@class_variable end def class_variable=(value) @@class_variable = value end def self.class_variable @@class_variable end def self.class_variable=(value) @@class_variable = value end end a = Practice.new b = Practice.new a.class_variable = 1 b.class_variable = 2 Practice.class_variable = 3 p a.class_variable p b.class_variable p Practice.class_variable #=> 3 #=> 3 #=> 3
モジュール変数
モジュールの中でクラス変数を定義すると、クラスをまたいで参照することができます(モジュール変数)
@@module_valiable = 1
module Practice_module @@module_variable = 4 class Module_class1 def module_variable @@module_variable end def module_variable=(value) @@module_variable = value end end class Module_class2 def module_variable @@module_variable end def module_variable=(value) @@module_variable = value end end end include Practice_module a = Module_class1.new b = Module_class2.new a.module_variable = 5 b.module_variable = 6 p a.module_variable p b.module_variable #=> 6 #=> 6
グローバル変数
どこからでも参照できる。乱用してはいけない。
$global
クラスインスタンス変数
先ほどインスタンス変数のところでちらっと出てた、クラスの中かつメソッドの外で宣言したインスタンス変数です。
クラスオブジェクト自身が持っているインスタンス変数という扱いです
@instance_valiable
class Practice @instance_variable = 1 def instance_variable @instance_variable end puts @instance_variable end #=> 1
クラスメソッドを使えばクラス自身から参照できます。
class Practice @instance_variable = 1 def self.instance_variable @instance_variable end end p Practice.instance_variable #=> 1
クラス定義の最中ならばputsもできます。
class Practice @instance_variable = 1 def instance_variable @instance_variable end puts @instance_variable end #=> 1
以上、変数とスコープのまとめでした。