vieweditattachhistorytopchangessearchhelp

第31回


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


 本連載では、名前は知っていてもなかなか触れる機会のないSmalltalkについて、最近話題のSqueakシステムを使って紹介しています。今回から新しく「コレクション」と呼ばれる、配列や文字列の仲間のオブジェクトについて触れるつもりだったのですが、もう一回だけ、ブロックにからめた処理制御について追加させてください。

 前回、処理をフォークさせて、かつ待ち合わせするために便宜的に用いた#forkAndWaitというメソッドがありました。その定義は次のようなものです。「forkAndWait」とタイプした後に選択してbrowse it (cmd+B)することで、いつでも閲覧できます。

BlockContext >> forkAndWait
   | semaphore |
   semaphore _ Semaphore new.                    "セマフォを作成"
   [self ensure: [semaphore signal]] fork.
   "レシーバの評価が終わり次第signalを送信する処理としてフォーク"
   semaphore wait                      "signal送信までここで待機"


 この内容に関連してセマフォ、そして#ensure:などの例外関係の処理について紹介します。

 前回も触れましたがセマフォは英語でsemaphore、日本語で腕木信号機とでもいうべきオブジェクトで、主にsignalとwaitというメッセージを送って使います。waitを受信したとき、すでにsignalを受け取っていれば、そのまま処理は進みますが、signalを受け取っていなければ別途signalを受け取るまでその処理を中断します。したがって、

Semaphore new signal; wait


はすぐに処理を終えますが、

Semaphore new wait


とすると、誰が送るわけもないsignalを永遠に待ち続けることになるので、結局、制御は戻ってこずハングアップします(cmd+ピリオドで中断してください)。

 先の#forkAndWaitでは、まず最初にセマフォを作り、続く式で、これからフォークさせる処理(forkAndWaitのレシーバであるブロック。selfに束縛されている)の最後に、そのセマフォへのsignal送信を追加(#ensure:は最後に触れます)してから改めてフォークしています。最後にセマフォにwaitを送って待ちに入り、フォークした処理でセマフォがsignalを受け取ると、その待ち状態が解除され処理が続行する…というからくりになっています。

 さて、話は変わって例外処理についてです。まず、例外を生じさせてそれを捕捉してみましょう。

| result |
result _ 3 / #(0 1) atRandom.
^ result


 このスクリプトは#(0 1) atRandom、つまり0か1かの選択の結果次第では完了しません。二分の一の確率で0が選ばれると3に「/ 0」というメッセージが送られることになり、その時点で「ZeroDivide」という例外が生じてとまってしまいます(ノーティファイアが出現)。

 ここでいったん、例外が生じる可能性のある処理をブロックで括ってみることにします。お馴染みのvalueを送信すれば直ちに評価できるので、上のスクリプトと同じことになります。

| result |
result _ [3 / #(0 1) atRandom] value.
^ result


 もちろん、これだけでは何も解決しないのですが、ここでvalueの代わりにifError: [...]というメッセージを送ることで、次のような対処が可能になります。

| result |
result _ [3 / #(-1 0 1) atRandom] ifError: [Float infinity].
^ result


 #ifError:はレシーバを評価し、例外が生じたときはパラメータとして添えられたブロックの評価値を代わりに用いるメソッドです。パラメータとして添えるブロックには2つまでのブロック変数を持たせることが可能で、それぞれから例外の種類と、例外を生じさせたメッセージのレシーバを知ることもできます。くわしくは#ifError:をbrowse it (cmd+B)して定義を眺めてみてください。なお、Float infinityは、浮動小数点数として振る舞う正の無限大値を返します。

 さて。例外が生じたときだけでなく、生じなかったときにでも、共通してきちんと済ませておきたい処理、というのもあります。先ほどの処理待ちセマフォへのsignal送信や、いったん開いたファイルをcloseする作業がよい例です。そんなときは、ifError: [...]の代わりに#forkAndWaitの定義で出てきたensure: [...]を送ります。

 次のスクリプトはtest1.txtというファイルを開いた後、「3 / 0」の結果の文字列表現を書き込んで、最後にファイルをcloseしています。

| file |
file _ FileStream fileNamed: 'test1.txt'.
file nextPutAll: (3 / 0) printString.
file close


 もちろん、3に「/ 0」というメッセージを送った時点で例外が生じますので以降の処理は続行できず、スクリプトはノーティファイアを出して止まってしまいます。このとき、ファイルは開いたままなので、続けて、

(FileStream fileNamed: 'test1.txt') edit


 などとした場合、このファイルを再び開くことができず、期待された作業ができない可能性があります。

 そこで最初のスクリプトを、

| file |
file _ FileStream fileNamed: 'test2.txt'.
[file nextPutAll: (3 / 0) printString] ensure: [file close]


というように#ensure:を使って記述しておけば、何らかの原因でブロック内の処理が正常に終了できなくとも、ファイルへのclose送信だけは確実に行なわれ、以後の同ファイルの取扱いに支障はない…というわけです。続けて次の式を評価しても、問題は起こらないはずです。

(FileStream fileNamed: 'test2.txt') edit


 ブロックは、まだまだいろいろなところに出てきますが、ひとまずこれで終わります。次回からは予告通り、コレクションについてです。

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


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

This page has been visited 932 times.