vieweditattachhistorytopchangessearchhelp

第41回


■SqueakではじめるSmalltalk入門   第41回


 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。

 前回は、先頃リリースされた日本語版Squeak「SqueakNihongo7」において、日本語文字列の扱いがどうなっているのかを見てみました。結論として、内部的に新しいクラスであるMultiStringが設けられてはいるものの、オブジェクトの持つ“多態性”のおかげで、我々ユーザーはその違いを意識することなく、従来の公式版でのアルファベットの文字列となんら変わらない扱いが可能、ということでした。オブジェクト指向万歳!ですね。

 文字列の他に、日本語の扱いで無視できないのがファイルの入出力です。これらについても文字列とほぼ同様のこと(つまり、日本語であることを意識せずに扱える…)が言えそうですが、ただ文字コードというものが絡んでくるため、じゃっかん複雑なことになりそうです。Squeakシステムでは、ファイルの入出力はストリーム(Stream)というクラスに属するオブジェクトを介して行なわれます。とりあえず今回はこの「ストリーム」というものに親しんでおきましょう。

 ストリームというのは“絶え間のない(データの)流れ”を意味します。コレクションはランダムアクセスが可能でしたが、ストリームはアクセスできる場所(ポジション)が決まっていて、そのポジションを必要に応じて前後に移動しながら、そこに位置するデータを読み書きします。磁気テープと磁気ヘッドの関係のようなイメージですね。なお、ファイルなどの外部情報へのアクセスを目的としないストリーム(内部ストリーム)は、コレクションをデータの置き場にして作られます。

 まず、ストリームを束縛するグローバル変数「MYSTREAM」を定義してください。これを使って、ストリームの振る舞いを調べたいと思います。

Smalltalk at: #MYSTREAM put: nil


 まず読み出し専用のストリーム(a ReadStream)を作って、今、定義したグローバル変数に束縛します。基になるコレクションを指定するために「on:aCollection」というメッセージをストリームのクラスReadStreamに送るか、あるいは、基にしたいコレクションに「readStream」というメッセージを送ります。

MYSTREAM _ ReadStream on: #('this' #is $a 10)
" もしくは "
MYSTREAM _ #('this' #is $a 10) readStream


 データ読み出しの場所(ポジション、position)は初期値では 0 になっています。

MYSTREAM position   " => 0 "


 「next」というメッセージを送るとデータを読み出し、positionをひとつ次へ移動します。メッセージ「next: anInteger」なら、指定した数だけデータを読みながらポジションを移動させ、読み出したデータは配列でまとめて返します。

MYSTREAM next       " => 'this' "
MYSTREAM position   " => 1      "
MYSTREAM next: 2    " => #(#is $a) "
MYSTREAM position   " => 3      "


 メッセージ「position: anInteger」の送信で直接移動もできます。
「peek」はポジションを移動させずにデータを読み出すためのメッセージです。

MYSTREAM position: 2; peek   " => $a "


 ポジションが最後にきたかどうかは「atEnd」を送信することで知ることができます。

World findATranscript: nil.   " トランスクリプトを表示 "
MYSTREAM reset.               " ストリームをリセット   "
[MYSTREAM atEnd] whileFalse: [Transcript cr; show: MYSTREAM next]


 ストリームが基にしているコレクションの内容を知るにはメッセージ
「contents」を送ります。

MYSTREAM contents   " => #('this' #is $a 10) "


 読み取り専用とは逆の、書き込み専用のストリーム「WriteStream」もあります。

MYSTREAM _ WriteStream on: String new


 書き込みに際しては「nextPut: anObject」を送信します。

MYSTREAM nextPut: $a.
MYSTREAM contents   " => 'a' "


 ただ、ここの例に限っては、基になっているコレクションが文字列(a String)なので、その要素は文字(a Character)でなければいけません。

MYSTREAM nextPut: 1   " => Error: Strings only store Characters "


 データをまとめて流し込むこともできます。このときは「nextPutAll:aCollection」を使います。この例では、パラメータとして文字列を与えてもよいでしょう。

MYSTREAM nextPutAll: #($b $c $d $e $f)
" もしくは "
MYSTREAM nextPutAll: ($b to: $f)
" もしくは "
MYSTREAM nextPutAll: 'bcdef'


 読み出し専用ストリーム同様、ポジションの任意の位置への移動も可能です。

MYSTREAM position: 2; nextPutAll: 'CD'; contents  " => 'abCD' "


 なお、書き込み専用ストリームでcontentsはポジションの位置までのデータしか示さないので、もし全データを参照したいときはsetToEndでポジションを既存の最後のデータの位置まで移動しておく必要があります。

MYSTREAM setToEnd; contents  " => 'abCDef' "


 余談ですが、書き込み専用ストリームは、長さ(要素数)固定のコレクションである文字列や配列の要素を自動生成したい場合などに重宝します。たとえば1から100までを要素に持つ配列は、次のように、ストリームを使って作ることも可能です。(もちろん1から100までなら、もっとシンプルな方法もありますが、あくまでもストリームを使った例ということで)

| stream |
stream _ Array new writeStream.
(1 to: 100) do: [:each | stream nextPut: each].
^ stream contents


 この手続きを抽象化した「SequenceableCollection class >>#streamContents:」というクラスメソッドもあります。内部的には上とまったく同じことをしているのですが(「streamContents:」を選択後、browse itでソースを確認してみてください)、一時変数の宣言が不要で、要素の生成ロジック(とストリームへの追加)だけを記述すればよいので全体のスクリプトもよりすっきりとしたものにできるでしょう。

^ Array streamContents: [:stream |
   (1 to: 100) do: [: each | stream nextPut: each]]


 以上を踏まえて、次回はファイルストリームとそこでの日本語データの扱いについて触れます。なお、今回使用したグローバル変数MYSTREAMは、次の式をdo itすれば削除できます。

Smalltalk removeKey: #MYSTREAM

このページを編集 (5454 bytes)


Congratulations! 以下の 1 ページから参照されています。

This page has been visited 871 times.