Chapter.03 変数
2017-10-02
ルビィの奇妙な変数
早速だがこんなコードを実行してみよう。どんな結果になるかな?
cat = 'にゃーん' animal = cat animal.slice!(1..-1) # Strig#slice!は文字列の指定位置を削除する puts cat # => "に"
んん? これはどういうことだろう。変数animal
は変数cat
をコピーして新しく作ったのに、animal
の中身が変更されるとcat
まで変わってしまった!
説明しよう。よく変数はデータを入れる箱だとか説明される。聞いたことがあるよね。それはわかりやすくした例えた、少し不正確だし概念だけの話だ。
ここでは実態について考えてみよう。まず、変数が初めてコード上に書かれた際に何が起こるかだ。
ハッピーバースディ!
変数が初めてコード内に登場するとメモリ上にデータを保存する領域を確保してそのアドレス(位置を特定する情報)が変数が紐づけられる。変数自身が持っているのはあくまでもメモリ上のアドレスだけだ。実際のデータ、それがどんなクラスでどんな状態なのか?はメモリ上に格納されている。例えるならWebページとurlの関係と同じだ、変数はurlってワケ。
(うん、本当に変数がメモリアドレスを持つのかは処理系によるよ。市民、あなたにこのチャプターも不要のようですね)
賢明な読者諸君はこの説明で冒頭のコードが何故こんな挙動になるのか理解出来たかもしれない。そう、変数cat
が持っているのは文字列"にゃーん"が格納されているメモリ上のアドレスであり、変数animal
に代入されたのもcat
が持っていた文字列"にゃーん"へのアドレスだからだ。つまり、catとanimalはメモリ上の領域を共有しているってワケ。
実際に見てみよう
cat = 'にゃーん' animal = cat animal = 'わんわん' puts cat # => "にゃーん"
では何故こんなコードは影響を受けないのかって? animal = 'わんわん'
が処理された時に起こるのはメモリ上のデータ操作ではなくて変数animal
に文字列"わんわん"が格納されている場所への新しいアドレスを持たせる、だからだよ。さっきのWebページとurlの例えで言えば、冒頭のコードはWebページを書き換えていて今度のコードはurlを書き換えるに過ぎないってワケ。
その違いがどこから生まれるかといえば、オブジェクトが新しく作られるのかどうかによるんだ。新しいオブジェクトが代入されるのなら違うメモリ領域が確保されるよ。例え代入されるオブジェクトの内容が全く同じでもね。
# まったく同じ内容のオブジェクトでもメモリ領域は別に確保される puts 'にゃーん'.object_id puts 'にゃーん'.object_id # => # 70195628982040 # 70195628981940 # 実のところ文字列リテラルによる記述もStringクラスのnewメソッドを使ってインスタンスを作るのと同じだ # 'にゃーん' => String.new('にゃーん') # FixnumやSymbol、Nilなど一部のオブジェクトは例外 puts 1.object_id puts 1.object_id # => # 3 # 3 # 複数の変数が同じオブジェクトへの参照を持つことも出来る cat = 'にゃーん' animal = cat puts cat.object_id puts animal.object_id # => # 70293867822040 # 70293867822040
object_idメソッドはオブジェクトがそれぞれ持つ固有のIDだ。本当は違うけど今回説明しているメモリのアドレスみたいなものだと思ってほしい。
1行目2行目のStringオブジェクトが違うIDを持っているのがわかるだろう。一方でFixnumのようなオブジェクトは何度試しても同じIDだ。これは当然のことでStringのようなオブジェクトは冒頭コードのようにslice!メソッドなど破壊的なメソッドによって変更できる(mutable)からだよ。
でもFixnumオブジェクトなど一部のオブジェクトは常に不変で変わることがないんだ。もし、そんなことが可能だったらすごく困ったことになるからね。
# 上で見た通り、この二つの変数は同一のオブジェクトと紐づいている cat_price = 100 dog_price = 100 cat_price.increment! # 破壊的に1加算するFixnumの架空メソッド puts cat_price puts dog_price # => # 101 # 101
えぇ? でもcat_price += 1
のような処理は普通に出来るだろうって? +=はメモリ上のデータを更新しているわけじゃない。これは再代入だ。つまり、cat_price = cat_price + 1
と同義ってワケ。

<< Chapter.02 メソッドを継ぐ Chapter.10 Webアプリを作る >>
[62]