酒匂と申します。
初心者向けの説明はなかなか悩ましいですね。

At 17:00 03/08/23 +0900, Take_tk wrote:
>次のような(初心者にとってはおそらく不可思議な)現象を、どのような言葉で
>説明すれば、分かりやすく、かつ、比較的正確に理解してもらえるか、を一緒に
>考えていただければ幸いです。
>
>--ruby
>#! ruby -Ks
>
>TEISUU = "定数の値"
>
>hairetu = [TEISUU]
>hensuu = hairetu[0]
>
>hairetu[0] << " <=> 配列の要素"
>hensuu     << " <=> 変数の値"
>
>p TEISUU     #=> "定数の値 <=> 配列の要素 <=> 変数の値"
>p hairetu[0] #=> "定数の値 <=> 配列の要素 <=> 変数の値"
>p hensuu     #=> "定数の値 <=> 配列の要素 <=> 変数の値"
>--

別件のウォーミングアップのため(笑)、
こんな説明を書いてみました。

コンパクトに書こうとしたのですが、結局
長くなってしまいました。。。御用とお急ぎでない方へ。
--------------------------------------------------------------------

Ruby における変数や定数とは、オブジェクトに付けられた名札と
考えることができます。

いま "hello" という文字列オブジェクトを考えてみましょう。
この文字列オブジェクトはそのままでは名前がありませんので、
繰り返し呼びかける(メソッドを呼び出す)のに不便です。

そこで hensuu という名札を用意して、"hello" オブジェクトに
貼り付けることにしましょう。Ruby で、あるオブジェクトに名札を貼り付ける
ためには、

名札 = オブジェクト

とします。よって

hensuu = "hello"

とすれば、以降 hensuu という名札を使って、"hello" で表される
文字列オブジェクトに呼びかけることができるようになります。

hensuu

と書けばちゃんと、その名札の貼られたオブジェクトが
呼び出されるので、

hensuu.length

と length メソッドを hensuu という名札の貼られた
オブジェクトに対して呼び出してやることができるのです。

さて、次に

TEISUU = hensuu

と書くとどうなるでしょう。先の式と同様に
= の左側の TEISUU は名札を表しています。
また = の右側は上で述べたように hensuu という
名札の付けられたオブジェクトそのものを
表しています。ということで、この式は

既に hensuu という名札が付いているオブジェクトに、
更に TEISUU という名札を貼り付けているのです。

ですから、この時点では最初の "hello" というオブジェクトには
hensuu, TEISUU という「二つの名札」が付けられていることになります。
つまりどちらの名札を使って呼びかけても、同じオブジェクトに
呼びかけることになります。

一つのオブジェクトには複数の名札をつけることができますが、
ある名札を使って、ある時点で呼び出されるオブジェクトは、一つしか
ありません。hensuu という名札をみてそれが貼り付けられている
オブジェクトを探すと、多くても一つしか見付からないということです
(場合によっては、まだその名札は適切なオブジェクトに貼られていないかもしれません)

ここで注意してもらいたいのは、当の "hello" オブジェクトは
自分にどのような名札が付いているかは知らないということです。
オブジェクトに付けられた名札は、そのオブジェクトに「呼びかけたい」
側の都合で付けられるだけです。

さて hensuu という名札と TEISUU という名札には、何か違いがあるでしょうか。
(実は Ruby にはこれ以外にも色々な名札があるのですが、ここでは簡単の
   ためにこの二種類の名札だけを考えています)

両者の一番の違いは TEISUU という大文字で始まる名札は、一度貼ってしまうと
他のオブジェクトに貼り直すことが推奨されないということです。

試しに以下のようなプログラムを用意して

# test01.rb
TEISU = "hello"
TEISU = "bye"

実行してみましょう。

$ ruby test01.rb
test01.rb:3: warning: already initialized constant TEISU
$

何か文句を言われましたね。これは、TEISU = "bye" を実行しようと
した際に、ruby インタプリタが "既に他のオブジェクトに貼られている
TEISU という名札を、別のオブジェクトに貼りなおしましたよ"
という注意を促しているのです。

先に述べたように TEISUU は大文字で始まる名札ですから、
一度貼ってしまうと別のオブジェクトに貼りなおすことは推奨
されません。このため ruby インタプリタは上のような
ワーニングを発生させたのです。

このような性質(定まったオブジェクトに貼られ続けることが期待される)
を持つことから、大文字で始まる
名札は「定数」と呼ばれています。

これに対して hensuu という名札は、自由に貼りなおす
ことができるので

# test02.rb
hensuu = "hello"
hensuu = "bye"

というプログラムを実行して、一度 "hello" オブジェクトに
貼られた hensuu という名札を "bye" というオブジェクト
に貼りなおしても文句は言われません。

このため hensuu という形式の名札は「変数」と呼ばれています。

今となっては「定数」「変数」という名称は相応しくないのかも
しれません。これらの言葉の原語は "constant", "variable"
ですが、厳密に言えば「定」「変」と言っているだけで、
特に「数」が対象だとは言っていないのです。
しかしこれは数学などの伝統を汲んだ「歴史的訳語」である
としか言い様がありませんね。

さて、ここでもう少し複雑な例を考えてみましょう。
以下のようなプログラムがあるとします。

----
#! ruby -Ks
  
TEISUU = "定数の値"

hairetu = [TEISUU]
hensuu = hairetu[0]

hairetu[0] << " <=> 配列の要素"
hensuu     << " <=> 変数の値"

p TEISUU     #=> "定数の値 <=> 配列の要素 <=> 変数の値"
p hairetu[0] #=> "定数の値 <=> 配列の要素 <=> 変数の値"
p hensuu     #=> "定数の値 <=> 配列の要素 <=> 変数の値"
----

このプログラムを実行すると、最後の3行のコメントにあるように

"定数の値 <=> 配列の要素 <=> 変数の値"
"定数の値 <=> 配列の要素 <=> 変数の値"
"定数の値 <=> 配列の要素 <=> 変数の値"

という出力が得られます。つまり

TEISU, hairetu[0], hensuu という3者の値が同じになっています。
これは何故でしょうか。

答えを先に言ってしまいますが、TEISU, hairetu[0], hensuu という
名札(hairetsu[0]は少し違いますが)は皆、同じオブジェクトに
貼られているのです。同じオブジェクトの値を見ているのですから
出力が同じになるのは当然ですね。

さてその様子を一つずつ見ていきましょう。

TEISUU = "定数の値"

これは TEISUU という「定数」名札を "定数の値" というオブジェクトに
貼り付けています。なので以降 TEISUU という名前を使ってオブジェクト
にアクセスできるようになりました。

hairetu = [TEISUU]

これはあるオブジェクトに hairetu という「変数」名札を
貼ろうとしているところです。

それではどのようなオブジェクトに
名札を貼ろうとしているかといえば

[TEISUU]

というオブジェクトということになるのですが、これは
TEISUU という名前が付いているオブジェクト(すなわち
最初の "定数の値" という文字列オブジェクト)を、
唯一の要素とする配列オブジェクトを表しています。

これで hairetu という名札を用いて、
配列オブジェクトにアクセスできるようになりました。

さて配列オブジェクトに [0] を使ってアクセスすると、
最初の要素(オブジェクト)を得ることができます。
よって

hairetu[0]

という表現は、hairetu という名札が貼られた配列オブジェクトの
最初の要素、すなわち "定数の値" オブジェクトを表すことに
なります。

このため

hensuu = hairetu[0]

を実行すると、hairetu という名札のついた配列オブジェクトの
先頭の要素(すなわち "定数の値" オブジェクト)に、
hensuu という名札が追加されます。

この時点で

TEISUU
hairetsu[0]
hensuu

が同じオブジェクトを指し示していることになりました。
これが理解できれば、あとは簡単です。

hairetu[0] << " <=> 配列の要素"
hensuu     << " <=> 変数の値"

この二つの行が実行されると、"定数の値" 文字列オブジェクトの内容に
順次 " <=> 配列の要素", " <=> 変数の値" が追加されて行きます。
# TEISUU, hairetsu[0], hensuu が同じオブジェクトに対する名札
# であることを思い出して下さい

この時点で、最初 "定数の値" だったオブジェクトの中身は

"定数の値 <=> 配列の要素 <=> 変数の値"

となっています。よって最後の3つの行の出力は
同じものになるのです。

なお繰り返しになりますが、ここに登場した "定数の値" オブジェクトは
自分が TEISUU と呼ばれたり、hensuu と呼ばれたり、あるいは
hairetu と呼ばれる配列オブジェクトの最初の要素になっていたりする
ことを全く関知していません。オブジェクトに付けられる名札は
呼ばれる側の事情に関わりなく、
「呼ぶ側の都合」で決められているだけだからです。

-----------------------------------------------------------------------
<Sako Hiroshi>  -- to design is human, design is our business 
   http://www02.so-net.ne.jp/~sakoh/  mailto:sakoh / ba2.so-net.ne.jp
   Designers' Den Corporation : and for now, No Peace, No Future.      
-----------------------------------------------------------------------