search
LoginSignup
4

More than 5 years have passed since last update.

posted at

updated at

文字列がオブジェクトであるとは、こういうことだーッ!(ぼかーん

プロローグ

ランバラルはアムロに戦場の厳しさを教えた。では文字列は我々にオブジェクトとは何かを教えてくれるだろうか。君はオブジェクトの涙を見る。

Smalltalkでの文字列

Smalltalkでは文字列もオブジェクトです。i番目の文字を取り出したり、あるいは書き換えたりすることができます。これは大概の言語でもそうだと思います。

残念なお知らせがあります。Smalltalkの文字列には文字を「追加」することができません。追加できないというのは、連結できないという意味ではありません。'abc'という文字列オブジェクトがあった時、'xyz'と連結すると'abcxyz'という新しい文字列オブジェクトが作られます。しかしOrderedCollection(JavaでいうArrayListみたいなもの)に要素を追加するのと同じように、文字列にもその文字列オブジェクト自体に要素を追加できるようにしたい。

テスト

つまり、このようなコードを書けるようにしたい。今回は処理系は Pharo 3.0 を使います。

workspace
| hello world |
hello := 'Hello'.
world := 'World'.
hello add: Character space.
hello addAll: world.
hello "==> 'Hello World'"

Pharo 3.0の標準のライブラリでは以下のようになります。

スクリーンショット 2014-12-14 7.27.14.png

叱られちゃった。てへ。

でもね、Smalltalkではエラーメッセージは異常終了ではないのですよ。まさにこれからプログラミングが始まりますよという、オープニングファンファーレなのです。(キリリ

Stringクラスを書き換える

文字列はオブジェクトです。オブジェクトというのは、ユーザー(プログラマ)が中の構造やコードを見ることができ、また、自由に書き換えることができるものを指します。さあ、文字列オブジェクトに「要素の追加の仕方」を教えてあげましょう。

Symbolを守ってあげる

SmalltalkではSymbol(書き換えができない文字列)は文字列の一種になっています。文字列が要素の追加ができるようになると、Symbolも要素を追加できてしまうので、Symbolには影響が及ばないように守ってあげましょう。

Symbol
add: aCharacter
    ^ self shouldNotImplement

Stringにadd:を教える

さあ、安心して文字列に要素の追加の仕方を教えましょう。とても簡単です。

String
add: aCharacter
    | expanded |
    expanded := self copyWith: aCharacter.
    self become: expanded.
    ^ aCharacter

copyWith:というのは、引数aCharacterで与えた文字を末尾に追加した「新しい文字列」を生成するためのメッセージです。次に、self become: expandedで、自分自身がその「新しい文字列」に成り代ります。内容をコピーするのではなく、オブジェクトとしてのアイデンティティを交換します。すると、新しい「自分自身」は、aCharacterを追加された状態になっています。

テスト

さあ、試してみましょう。

スクリーンショット 2014-12-14 7.25.10.png

バッチリですね。変数helloを書き換えたのではなく、'Hello'という文字列自体がどんどん伸張していきます。

ええい、要素の削除はできんのか!

はい、同じやり方でできます。例えばこんな感じ。

Symbol
remove: aCharacter ifAbsent: errorBlock
    ^ self shouldNotImplement
String
remove: aCharacter ifAbsent: errorBlock
    | index shrinked |
    index := self indexOf: aCharacter ifAbsent: [ ^ errorBlock value ].
    shrinked := (self copyFrom: 1 to: index - 1) , (self copyFrom: index + 1 to: self size).
    self become: shrinked.
    ^ aCharacter

スクリーンショット 2014-12-14 7.34.10.png

落とし穴に落ちるとは、こういうことだー!(ぼかーん

調子に乗ってリテラル文字列を書き換えるとひどい目に合います。例えばコンパイラがリテラルの生成をケチると、こういう目にあいます。

スクリーンショット 2014-12-14 7.36.03.png

最後、'Hello'という文字列リテラルを返しているので、結果は'Hello'になるはずです。しかし何故か'Hello World'が。これは2箇所の同じ字面の文字列リテラルを1つのオブジェクトで使いまわしているからですね。要素の追加に限らず リテラル文字列は書き換える前にコピーして使いましょう。

スクリーンショット 2014-12-14 7.37.49.png

おわりに

標準ライブラリであろうが、文字列のような基本的なオブジェクトであろうが、欲しいと思った機能は実現する。実現できる。それがオブジェクト指向プログラミングの楽しさだと思います。鳴かぬなら鳴き方を教えてやろうホトトギス。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
4