Smalltalk-72で遊ぶOOPの原点:簡易スケジューラーを作る
アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
obset
を活用して簡易スケジューラー spacewar
を定義
前回の動作チェックで使った repeat
の代わりに、新たに spacewar
アクションを定義し、これにその役割を担わせることにしましょう。
こんな仕組みと動きを想定します。
objects
というセットもどき(obset
)をクラス変数として持ち、spacewar schedule obj
で宇宙船や、将来は魚雷などのオブジェクトをobjects
に追加spacewar delete obj
で指定したオブジェクト、spacewar delete all
で全てのオブジェクトのスケジュールを放棄spacewar run
でobjects
内のオブジェクトに順に制御を渡す
クラス変数 objects
に代入される obset
は、ALLDEFS(ブートストラップ)で定義されている、要素をユニークなもののみに保つセットの性質も有するシンプルな動的配列オブジェクトです。
☞set ← obset.
でインスタンスを生成し、set ← elem
とすることで、elem
がはじめてなら追加、すでに追加済みなら無視します。
obset
は、この elem
のユニーク性を維持する ← elem
だけでなく、無条件で追加する add elem
もあるので、こちらを使えば要素の重複を許す通常の動的配列としても使えます。
ただし、動的“配列”とはいうものの、インデックスでのアクセスなど配列の基本的な機能はいっさい用意されていないので、そういう用途には vec
で内部表現のベクター vector
のインスタンスを引っ張り出して使うという割り切った仕様になっています。
前述のとおり、ALLDEFSウインドウ、もしくは show obset
でその定義を見ることができます。
後の Smalltalk-80 で導入される do:
や Ruby の each
に相当する map
も素朴な実装ながら備えられています。
ところで、組み込みの配列 vector
にも map
はあるにはあるのですが、この obset
の map
とは違って、各要素に map
の引数となる配列内に収めたシンボルをメッセージとして送るだけという別の仕様になっています。こちらは、Smalltalk の do:
や Ruby の each
でブロックの代わりにシンボルを与えたときの使い方に似ています。同じ map
でもぶれがある雑さがいいですね^^;
spacewar
アクションはこんなふうに定義してみました。
to spacewar x y z : : objects ( (null objects ? ("objects _ obset)) %schedule ? (objects _ :#) %delete ? (%all ? ("objects _ nil) objects delete :#) %run ? (repeat (objects map "("x _ vec[i]. x))) ) disp clear "s1 _ spaceship 'Jimmy' 5 30 false. "s2 _ spaceship 'Beth' `5 `20 false. spacewar schedule s1. spacewar schedule s2. spacewar run
前回同様に2艇の宇宙船 s1
s2
を定義して schedule
後、run
してみましょう。
esc
キーで停止できます。