MogLog

メモというか日記というか備忘録というか

【Ruby】代入

「代入」の概要
代入式は、1つ以上の左辺値の値のために1つ以上の値を与える。
左辺値:代入演算子の左側にかけるものを指す。Rubyで左辺値にあたるのは、変数、定数、属性、配列要素である。
右辺値:代入演算子の右側にかけるものを指す

Rubyには代入式の形式が3種類存在する。
(1) 単純代入
1個の左辺値、「=」演算子、一個の右辺値で構成される。

x = 1   # 左辺値xに値1を代入する 


(2) 自己代入
変数の現在の値に他の何らかの演算を適用し、その結果によって変数の値を更新する式の略記法。

x += 1 # 左辺値xに値x+1を代入する


(3) 多重代入
左辺値か右辺値が複数個ある代入式である。

x,y,z = 1,2,3 # xに1、yに2、zに3を代入する


代入演算子について
代入演算子は「右結合」とよばれる性質を持っている。
この性質により、1つの式に複数の代入が含まれている場合には「右から左に」式が評価される。
この性質を上手く利用すると、以下のように連鎖的に代入を行うことができる。
以下の式は「yに値0を代入した後に、xに最初の代入式の値(ここでは0)を代入する」ことを示す。

x = y = 0 # xとyに0をセットする


代入と副作用
代入式は、それ自体の値よりも、代入によって変数に値がセットされ、それによりプログラムの状態が変化を受けることのほうが重要である。
プログラムの状態に対する影響のことを代入の「副作用」と言う。
多くの式は副作用を持たず、プログラムの状態に影響を与えない。そのような式を「冪等(べきとう)」と言う。冪等であることは、式を繰り返し評価しても、毎回同じ値が返ってくることを意味する。さらにこれは、式を評価しても他の式の値に影響を与えないことにもつながる。

# 以下2つの式には副作用が無い
x + y
Math.sqrt(2)

# 代入は冪等ではない
x = 1 # xを使う他の式に影響を与える
x += 1 # 評価されるたびに異なる値を返す


変数への代入
変数への代入は、特に変わったことをするわけではない。単純に指定された値が変数にセットされると思えば良い。唯一、問題となりうるのは、「(1)変数宣言の問題」と、「(2)ローカル変数とメソッド名が曖昧である」という点である。

(1) Rubyには、変数を明示的に宣言するための構文が無い。変数は値を代入されたときから存在するようになる。

(2) Rubyでは、ローカル変数名とメソッド名を区別することができない。
例えば、「x」という式があったとする。この場合、xが、「xという名前のローカル変数を参照している」のか、「selfのxというメソッドを参照している」のか、Rubyは判別ができない。
この曖昧さを解決するために、Rubyは識別子に対する代入を検出していれば、その識別子をローカル変数として扱い、そうでない場合はメソッド参照として扱うようになっている。

以下のコードは、これらの特徴を示す。

class Ambiguous
     def x; 1; end # 必ず1を返すxというメソッド

     def test
          puts x # まだ変数を検出していないので、上のメソッドを参照する。結果、「1」を出力する

          # "if false" となっているので、下の式は実行されないが、
          # パーサはこれを検出してメソッドの末尾までxを変数として扱う
          x = 0 if false

          puts x # xは変数だが、代入されていないのでnilを出力する

          x = 2 # この代入は評価される
          puts x # そこでこの行は2を出力する

     end
end


定数への代入
定数は変数とは明らかに異なる。
定数の値は、プログラムの実行を通じて一定の値に保たれるつもりで設定されている。

【定数への代入のルール】
(1) 既に存在する定数に代入すると、Rubyは警告を発行する。しかし、代入自体は実行するので、定数は実際には一定ではない。
(2) 定数への代入は、メソッド本体の中では認められない。Rubyはメソッドは1度以上呼び出されるものだと想定している。仮にメソッド内で定数に代入できるようにすると、メソッドは初回を除き、毎回警告を発する。そのため、これを最初から認めないことになっている。
→ メソッドは何度も呼び出される可能性があるにもかかわらず、定数は一度しか代入をすることができない性質を持っている。2つの性質の間で整合性を取るために、メソッド内での定数宣言を認めていないということだろう。

また、定数は変数とは違い「未初期化」状態が存在しない。
Rubyインタプリタが実際に代入式を実行するまで、定数は作成されない。
この性質から、定数は存在するならば何らかの値を代入されていると考えて間違いないということになる。

# 以下の式は実行されないが、nという変数は初期化される(nilを持つ)
n = 100 if false

# 以下の式は実行されないので、定数Nは作成されない
N = 100 if false


属性と配列要素への代入
Rubyでは、属性や配列要素への代入は、実際にはメソッド呼び出しの略記法である。
<属性への代入>
オブジェクト「o」がメソッド「m=」を持っているとする。
このとき、o.mは代入式の左辺として使うことができる。

o.m = v # 値vを代入する

# Rubyインタプリタにより、上の式は実際には、以下のように変換される
o.m=(v)

つまり、このコードはメソッド「m=」に値vを渡している。
「m=」のようなメソッドには「m」というメソッドが付随していることが多い。
「m=」はセッターメソッドと呼ばれ、「m」はゲッターメソッドと呼ばれる。
オブジェクトがこの2つのメソッドのペアを持つ時、このオブジェクトは「属性mを持っている」と表現する。

<配列要素への代入>
属性と同様に、配列要素への代入も、メソッド呼び出しによって行なわれる。
オブジェクトoが『=』という名前のメソッドを定義している場合、『o[x] = y』という式は、実際には次のように評価される。

o.=(x,y)

オブジェクトが3個の引数をとる『[]=』メソッドを定義している場合、角カッコの間に2個の添字をいれることができる。

# 以下2つの式は同義
o[x,y] = z
o.[]=(x,y,z)


自己代入
自己代入は、「他の演算と代入を組み合わせた形式の代入」のことである。

x += 1

# 「+=」は実際にはRubyの演算子ではなく、以下の式の略記法にすぎない
x = x +1

【自己代入あれこれ】
(1) 自己代入は多重代入とは併用できない
(2) 左側に単一の左辺、右側に単一の値が置かれている場合のみ利用可能
(3) 定数に対して自己代入を行うと、定数に値を代入し直すことになり、エラーを発するので利用しないほうがいい
(4) 自己代入は属性に対しても利用できる

# 以下2行は全く同じ意味
o.m += 1
o.m=(o.m()+1)

# 以下2行も同じ。左辺は配列でもおk
o[x] -= 2
o.[]=(x, o.[](x) - 2)

「擬似演算子」とあるとおり、本物の演算子ではなく、他の演算子を使った式の略記法にすぎないことを覚えておく。