値と実体
AとBのふたりの人物がいます。
Aは私です。つまりはるかです。
"はるか" a =
Bも「はるか」ですが、私ではありません。 舞台女優です。1
"はるか"
a = "はるか" b =
aとbは名前が同じですから、値は同じです。
# "はるか"
a.inspect # "はるか"
b.inspect # true a == b
私はミーミルヨコハマのエンジニアです。 一方、彼女は劇団エプオート2の女優です3。
mimir_yokohama_engineer = a
a_man = a
epuort_actress = b the_woman = b
もちろん、値は同じです。
# true mimir_yokohama_engineer == epuort_actress
しかし、私と彼女は別の人物です。 一方、ミーミルヨコハマのエンジニアと私は同一人物です。 その実体を示すIDを見てみましょう。
"はるか"
a = "はるか"
b =
mimir_yokohama_engineer = a
a_man = a
epuort_actress = b
the_woman = b
p a.object_id
p b.object_id
p mimir_yokohama_engineer.object_id
p a_man.object_id
p epuort_actress.object_id p the_woman.object_id
結果は…
47380071672020
47380071672000
47380071672020
47380071672020
47380071672000
47380071672000
一見同じに見えるような値になってしまいましたが、よく見ると下2桁が20
と00
にわかれています。 つまり、私と彼女で別のIDを持っているということになります。
"はるか"
a = "はるか"
b = # false a.object_id == b.object_id
けれど実体が同じならIDも同じです。
"はるか"
a =
mimir_yokohama_engineer = a# true a.object_id == mimir_yokohama_engineer.object_id
実体の変更
彼女が名前表記をカタカナに変更したとしましょう。4
"はるか"
b =
epuort_actress = b
"はるか", "ハルカ") b.sub!(
変更されているのはb
の値だけではありません。 実体はひとつ(彼女は分裂したりしませんから!)ですから、epuort_actress
の値もちゃんと変わっています。
# "ハルカ"
b.inspect # "ハルカ"
epuort_actress.inspect
# true b.object_id == epuort_actress.object_id
しかし、代入した場合は別です。 この場合は計算した結果である新しい値を代入するからです。
"apple"
a =
fruit = a
"apple", "waffle")
a = a.sub(
# "waffle"
a.inspect # "apple" fruit.inspect
参照と値
値は実体が何であるかではなく値が何であるかということです。 これは同じものがひとつしか持てないわけではありません。例えば日本には何人もの山田太郎さんが(多分)います。 これらはそれぞれ別の人ですが、名前という値は同じです。
しかし、山田太郎さんはそれぞれ様々な面を持っていることでしょう。「会社員」山田太郎、「夫」山田太郎、「息子」山田太郎などなど… これらは何でそれを指し示しているかは異なりますが、指し示している先はひとりの山田太郎になります。
値はそれぞれ実体を持っています。 コンピュータ的には、その実体はメモリ上にあります。
変数は値そのものを持っている場合もありますが5、変数は実体を参照していることもあります。
この参照が「リファレンス」で、参照されている実体が「リファレント」になります。
これは次のようになります。
- 即値を持つ場合、そもそも同じ実体を複数の変数で共有することができないので、
a = b
のようにしたときには値がコピーされる - リファレンスを代入した場合、
a = b
は同じリファレントを指すリファレンスになる
a = b
がどのように動作するかについては言語によって異なります。
Ruby及びJavaScriptは原則としてリファレンスを扱います。 一部即値を持つもの(例えば数値)に関してはリファレントを変更するような操作を許していないためそれを意識する必要はなくなっています。 「リファレントそのものを変更するような破壊的メソッドを呼び出した場合、全てのリファレンスから得られる結果が変わる」と統一されているのです。
例えばPerlにおける
$str =~ /apple/waffle/;
は$str
そのものを変更しますが、JavaScriptの
.replace("apple", "waffle") str
はstr
を置き換えた文字列を値として返します。str
自身は変更されません。6
Perlでは通常値は即値を持つ一方、変数に対するリファレンスを手動で得ることもできます。 そのような振る舞いをもつプログラミング言語は非常に稀ですが、Perlにおいて最も苦痛を招くポイントでもあります。7
値渡しと参照渡し
これを気にしなければならない言語なんてだいぶ限られているのですが、C言語のみならずPerlとPHPが該当しますから油断なりません。
リストを渡すことを考えてみましょう。 Perlで次のようなコードを考えてみます。
sub pushlist(@) {
my @list = @_;
push(@list, "D");
}
@l = ("A", "B", "C");
@l); pushlist(
このとき@l
の値は
("A", "B", "C")
です。
では、今度はリファレンスを渡してみましょう。 Perlは\
を前置することでリファレンスが得られ、$
を前置することでリファレンスからリファレントが得られます。
sub pushlist($) {
my $list = @_[0];
push(@$list, "D");
}
@l = ("A", "B", "C");
@l); pushlist(\
今度は
("A", "B", "C", "D")
になりました。
Perl4まではサブルーチンの外側の変数を直接書き換える、ということがよく行われていたため意識することもなかったのですが、 Perl5からは変数の受け渡しをするようになりました。
値で渡した場合は、渡されているのは同じ値を持つコピーであるため、変更を加えても渡した元に対しては影響がありません。
対して参照(リファレンス)で渡した場合は同じ実体であるため、変更を加えると呼び出し元からみても変更が反映されています。