たけ(tk)です。

* すみません、間違ってボタンを押してしまったようで、書きかけのものを送っ
てしまいました。

[ruby-list:38183] String << の動作につきまして にて 
kuto / d-itlab.co.jp さん 曰く:

> (1) % ruby -e "s = ARGV[0]; puts s.frozen?"
> false

 これは ARGV[0] が 空(nil)だからでしょう。
 文字列が入っている場合にはtrueになります。

G:\DOWNLOAD\ruby-1.8.0-20030404-i386-mswin32\bin>ruby -e "s = ARGV[0];
puts s.frozen?; p ARGV[0].frozen?" foo
true
true

> (2) % ruby -e "s = ARGV[0]; s << 'hoge'; puts s" foo
> -e:1:in `<<': can't modify frozen string (TypeError)
>         from -e:1

  これは『Rubyの冒険 旅立ち編』のp.204からp.216の10ページほどかけて説
明した「破壊的メソッド」と「オブジェクトの共有」の問題です。

 簡単に言うと、

 (1) s = ARGV[0]

の時点で、変数sの中身のオブジェクトはARGV[0]の中身と同じもの(文字列オブ
ジェクトの"foo")を差すようになります。(オブジェクトの共有)。

 ARGV[0]が文字列である場合には、freeze されています。(ruby1.8)したがっ
て、変数sの中身のオブジェクトはそれと同一のオブジェクトなので、当然なが
らfreezeされています。

 (2) s << 'hoge'

文字列オブジェクトが左側にある<<演算子はString#<<なので破壊的メソッドで
す。破壊的メソッドでは変数に入っているオブジェクト自体を変更しようとしま
す。これはobject_idメソッドを使ってオブジェクトの同一性を確認すればわか
ります(後述)。

変数sとARGV[0]のオブジェクトが同一である場合には、変数sで破壊的メソッド
を使用すると、ARGV[0]のオブジェクトも変更しようとすることになります。

しかし、(1)で見たように、そのオブジェクトはfreezeされているので、エラー
になります。

> (3) % ruby -e "s = ARGV[0]; s += 'hoge'; puts s" foo
> foohoge

+=演算子の場合には新しいオブジェクトを作成して、変数sに新しいオブジェク
トを入れます。新しく作成したオブジェクトに入れ換えることになるので、
ARGV[0]のオブジェクトには影響を与えません。もともと変数sに入っていたfreeze
された文字列は追い出されるだけということになります。

----

<<演算子と+=演算子の違いはオブジェクトのIDを調べてみるとはっきりします。

<<演算子の場合にはオブジェクトのIDが変わりませんが、+=演算子の場合にはID
が変わります。つまり、別のオブジェクトに入れ替わります。

G:\DOWNLOAD\ruby-1.8.0-20030404-i386-mswin32\bin>ruby -e "s = 'foo'; p s.id;s<<'bar';p s.id; p s"
21121572 ←オブジェクトのIDは変わらない
21121572 ←つまり、同じオブジェクト
"foobar"

G:\DOWNLOAD\ruby-1.8.0-20030404-i386-mswin32\bin>ruby -e "s = 'foo'; p
s.id;s+='bar';p s.id; p s"
21121560 ←元のID
21121524 ←オブジェクトのIDが変わる。オブジェクトが入れ替わった。
"foobar"

take_tk = kumagai hidetake