Smalltalk-72で遊ぶOOPの原点:「sin」「cos」の実装
アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
宇宙船の位置を更新する moveship
で使われている sin
と cos
spaceship
クラスで、宇宙船の位置を更新するプロシージャがこちらの moveship
です。
宇宙船の速度と向きから新しい位置を算出するのに三角関数が使われていますが、abs
、sqrt
すら無い Smalltalk-72 に三角関数があるはずもないので例によって雑に(^^;)実装しておきます。
余談ですが、正弦関数 sin
(sine
) にからめては、1975年の「Personal Computing(パーソナル・コンピューティング)」にアラン・ケイによるこんな記述がありますのでご紹介します。
外から見ると、ブラックボックスが返事を返すために使っている方法(メソッド)については、私たちはその詳細を知りえない。たとえば私たちが次のようにリクエストしたとき
sine 30
その返答がルックアップテーブルから得られた値なのか、チェビシェフ近似で計算されたものなのか、無限級数の和を使って計算されたものなのか、あるいはこれらの組み合わせなのかはわからない。そして実際、私たちは、期待される返答が一貫して、素早く、私たちが行っているかもしれない他のことを妨げることなく戻ってくる限り、まったく気にする必要がない。
欲求(意味的な概念)と方法(実用的な概念)を分離することが、Smalltalkの中心にある原則である。
ここでは、無限(?)級数の和(テーラー展開)を使うことにします。
まず整数で結果を返す階乗 nfact
が必要です。実装は簡単ですが、16ビットの符号付き整数はすぐにオーバフローしてしまうので 7
が限度な点に要注意です。(もとより Smalltalk-72 には、Smalltalk-80 で実装された多倍長整数 LargeInteger
も、小整数 SmallInteger
からそれへの自動コーション機構もまだありません。)
to nfact acc n ("acc _ 1. for n to (:) do ("acc _ acc * n) !acc)
そして sin
と cos
はこんな感じにしました。
"PI _ 3.14159265 to sin acc x n m ( "x _ (:) mod 360. (180 < x ? ("x _ x - 360)) (90 < x ? ("x _ 180 - x) `90 > x ? ("x _ `180 - x)) "x _ (PI / 180) * x. "acc _ 0.0. for n _ 0 to 3 do ( "m _ 1 + 2 * n. "acc _ acc + ((`1.0 ipow n) * (x ipow m) / nfact m)) !acc) to cos (!sin 90 + :)
この算出方法は、角度の絶対値が大きくなると誤差が大きくなるので、与えられた角度は -90
~ 90
に正規化してから計算しています。なお π
はグローバル変数 PI
で与え、cos
は位相をずらすことで定義しています。
ipow
は float
に組み込みの整数乗の演算です。show float
で表示される float
クラスの記述の中の ᗉipow ⇒
セクションで定義を参照できます。
え? ブラックボックスなのにこんな簡単に定義が読めたり書き換えられても良いのか?
いえいえ。そうすることに合理的理由があるのなら、オブジェクトは内部をさらしたり、中味の作り直しを許すことも(まあ好ましくはないわけですが)アラン・ケイの「オブジェクト指向」の枠組みから必ずしも外れてはいないのです。
(改めて「moveship」の実装へ続く)