Smalltalk(4)
前回は、枕に体組成計を買ったって書いた。体の中にある脂肪は電気を通しにくいんで、伝導度を 測れば脂肪量も分かるって事だった。その測定結果がインピーダンスで出てくるってのが、ちと おいらにはまゆつばっぽく感じられる。リアクタンス(誘導性らしいけど)が本当に含まれて いるんかいな?
人体を、両手、両足、胴体の5つのパーツにモデル化してるようだけど、どこに誘導成分が 有るんかいな? 手足の長さと太さでコイルを形成するの? コンデンサを両手に持ったら、共振 するの? こんど、タニタのおばちゃんに聞いてみたいぞ。
まあ、そんな馬鹿話はどうでもよくて、折角Smalltalkに首を突っ込んでいるんで、インピーダンスを Smalltalkが扱えるか確認してみる。
3 + 4i abs -> 7.0
実部が3、虚部が4の複素数の絶対値は、7ですって。何となく、単純に足し算してるなあ。 左から右に解析が進んで、メッセージ(+)の引数が(0 + 4i)absって解釈されてんだな。
z := 3 + 4i z abs -> 5.0 (3 + 4i) abs -> 5.0
こうしないとダメなのね。これって、式の解析をさぼったから? それとも深い訳があるの? 教えて、アラン・ケイさん。
ついでなので、複素数誕生の瞬間も見ておく。
new ^ self real: 0 imaginary: 0
クラスのインスタンス作成にこんなのと、
abs: aNumber1 arg: aNumber2 | real imaginary | real := aNumber1 * aNumber2 cos. imaginary := aNumber1 * aNumber2 sin. ^ real + imaginary i
こんな、極座標形式が載ってた。クリック・クリックでソースに当たれるのは有り難いな。
factor
少々Smalltalkも食指ぎみになってきた、そんな折文法がちょいと(おいら的には)似てると思う、 factorなんてのに手を伸ばしてみた。 連鎖性言語一族らしい。詳しい事は、Factor 座談会で。
間違った事をすると、道しるべのWindowが出て来るのが、Smalltalkと似てると思うんだ。なんだ それだけしか共通点が無いんかいな。バイナリーが用意されてるけど、unixの伝統に習い、ソース から入れるのが正しい道です。
git clone http://factorcode.org/git/factor.git
ソースを落としてきて、コンパイルすれば良いと思ってやったら、出来上がったバイナリーだけじゃ 起動しないのね。イメージが必要なんですって。そんな所もSmalltalkと良く似てるな。 正式な作り方は、Building from a clean branch を参照。ちゃんとソースを読むと(取っ掛かりは、build-support/factor.shのあたり)、イメージの 作り方も分かりそう。
Linuxなんかだと楽チンだけど、OpenBSDとかだとちと面倒。最後は、イメージの種銭をサイトから 取ってきて、下記のように太らせていく。きっとsqueakなんかも同じ手法を取っているんだろうな。
$ ./factor -i=boot.unix-x86.32.image : Loading resource:basis/help/handbook/handbook.factor Loading resource:basis/help/cookbook/cookbook.factor Loading resource:basis/help/tutorial/tutorial.factor Core bootstrap completed in 9 minutes and 37 seconds. Bootstrap completed in 9 minutes and 37 seconds. Bootstrapping is complete. Now, you can run Factor: /home/sakae/src/factor/factor -i=factor.image Loading vocab:bootstrap/finish-bootstrap.factor
とまあ、OpenBSDでもfactor出来るようになったんだけど、Smalltalkと似てる点がもう一つ有ったよ。 それは、連鎖性言語/左から右へ評価 されるって事。これで、Smalltalkの左から右方式アレルギーもきっと払拭出来るだろう。詳しい解説は、 まず、HnadBook参照そして、 こちらとか あちらを見れ。
Little Smalltalk
前回リンクしたモサ伝を経由して、世の中には、Little Smalltalkなんてのが有る(有った)事を 知った。すっきりしていそうなのでソース嫁にはうってつけと思い探してみたよ。そしたら、 こちらで Little Smalltalk v.3 紹介されていた。なお、Littleの作者様は、最近ではJavaに進出して、 The Athena Smalltalkなんてのを作っているようです。小さくて 面白そうですね。
ソースも手に入ったし、Smalltalk系が何も入ってないOpenBSD上でやってみるか。ああ、勿論 OpenBSDにもportsとしてsqueakは用意されてますよ。
$ grep squeak CATLOG squeak-funsqueak-3.10alpha7.tgz squeak-image-3.10.2.7179p0.tgz squeak-sources-3.9.tgz squeak-vm-3.10.1p1.tgz
squeakには目もくれずに小さいのに挑戦します。tar玉は、展開するとソースをぶちまけてくれるので、dirを掘ってそこに展開する事。 readmeが付いてたので読んでみると、manの原稿を読むよりタイプセッターにかけてから読もうねって 書いてあった。
groff -ms -Thtml manual.ms > st.html groff -ms -Thtml install.ms > install.html
しましたよ。後は、w3m install.htmlとかすれば良い。 昔々の作品なので、アタリとか、Turbo C compiler を入れたIBM PC、はたまた、Lightspeed C を 入れたMacintoshでも動くとな。Lightspeed Cなんて、懐かしいねぇ。昔使ってたよ。
Macの場合は、GUIじゃないと動かないので、GUIの部分は別途手配してね、なんて書いてある。
3. The Standard Window Package There is an experimental windows style interface based on Guido van rossums standard window package. This permits the system to work on top of X-windows, as well as the macintosh.
この後、Guidoへの連絡先としてDEC宛のメールアドレスやらアムステルダムへの地上メール住所 なんかが書いてあったよ。のんびりした時代だったのねぇ。今じゃ、Guido氏もPythonで忙しい だろうに。
早速コンパイルしてみると、
$ make bsdtty : primitive.c:52: error: conflicting types for 'time' /usr/include/time.h:128: error: previous declaration of 'time' was here primitive.c: In function 'zeroaryPrims': *** Error code 1
こんなエラーを喰らってビルドに失敗してしまう。install.htmlにはgccを使う場合注意って 書いてあったけど、本当だ。さてはて、エラーを一つ一つもぐら叩きするのもかったるにので、 一遍に修正したいぞ。確か私の記憶では、emacs上でコンパイルすれば楽出来るはずだったぞ。
M-x compile を指定して、出てきた質問に、make -k bsdtty と答える。これで、画面が割れて ワーニングは黄色、エラーは赤でリンクが出て来る。赤の部分を修正しろ。 修正ったて、コピペするだけだけど。emacsでのコピペのキーバインドを良く忘れるのでメモ。
C-space コピーしたい所に照準を合わせ C-n コピー範囲を決め ALT-w コピー、後は、コピー先まで移動して CTL-y 貼り付け
切り貼りの様子は、巻末に載せておきます。で、stが出来上がったので起動してみると
$ ./st cannot open image systemImage Abort trap (core dumped)
イメージが要るんか。一応、臨場しとく。
$ gdb -q st st.core Reading symbols from /home/sakae/src/little-st/st...done. [New process 2143] Core was generated by `st'. Program terminated with signal 6, Aborted. #0 0x0984e98d in kill () from /usr/lib/libc.so.65.0 (gdb) bt #0 0x0984e98d in kill () from /usr/lib/libc.so.65.0 #1 0x098ba545 in abort () at /usr/src/lib/libc/stdlib/abort.c:68 #2 0x1c00caae in sysError (s1=0x3c000d0e "cannot open image", s2=0xcfbd3554 "systemImage") at tty.c:22 #3 0x1c00c9d6 in main (argc=1, argv=0xcfbd362c) at st.c:57
原因はイメージが無いって事だな。Makefileを見て思い出した。PATHを自dirに通して、もう一度。
$ PATH=.:$PATH $ make bsdtty make "CFLAGS=-g -DB42" "LIBS=" "INTERFACE= tty.o" initial st `initial' is up to date. `st' is up to date. initial basic.st mag.st collect.st file.st mult.st tty.st basic.st: mag.st: collect.st: file.st: mult.st: tty.st: initialization ............finished
これで、イメージが出来たぞ。実行してみる。
$ ./st Little Smalltalk, Version 3.04 Written by Tim Budd, Oregon State University object count 2989 > 3 + 4 7 object count 2993 > gc nil object count 2995
ああ、やっと動いたわーい。
観察会
例によって観察会です。イメージが読み込まれる所
60 imageRead(fp); (gdb) s imageRead (fp=0x2b466260) at unixio.c:43 43 ignore fr(fp, (char *) &symbols, sizeof(object)); (gdb) n 44 i = 0; 46 while(fr(fp, (char *) &dummyObject, sizeof(dummyObject))) { 47 i = dummyObject.di; 49 if ((i < 0) || (i > ObjectTableMax)) 51 objectTable[i].class = dummyObject.cl; 52 if ((objectTable[i].class < 0) || 57 objectTable[i].size = size = dummyObject.ds; 58 if (size < 0) size = ((- size) + 1) / 2; 59 if (size != 0) { 65 objectTable[i].memory = (object *) 0; 46 while(fr(fp, (char *) &dummyObject, sizeof(dummyObject))) {
mainの中からimageReadが呼ばれるのね。で、objectTableってのに書き込んでいるんか。これが 心臓部か。
(gdb) p objectTable[0] $2 = {class = 30, referenceCount = 1, size = 0, memory = 0x0} (gdb) p objectTable[1000] $3 = {class = 8, referenceCount = 13, size = -3, memory = 0x8a270c36}
中身拝見
折角なので、何か作ってみる。簡単な所でBMIが良いかな。BMIは答えが小数点付きで戻って くる事にしよう。そうすると、BMI計算機は、Float族になるんだな。違うよ、それはC言語の 考え方だよ。小数点付きの体重(と身長)を与えるんだから、Float族だよ。ああ、めんどう臭い。
まずは、定義だな。Float族にメソッドを追加するって事で、まんま、
Float addMethod
します。するとviが自動起動するんで、(editor <- 'emacs' とか設定すると、重いのが立ち上がってくる) 定義してあげます。書き込んでviを閉じると登録終了。文法違反とかあると何回もやり直し させられます。成功したって、実行時に文句を言われる事があります。
余談だけど、おいらがpythonする時は、パイソン用のshellである、ipythonを使います。こいつも edit hoge.pyとかやると指定のeditorが起動し、閉じると、即スクリプトが実行され、エラーが あると、スタックとレースして、馬鹿にしてくれますよ。
実行してみます。
> 68.0 bmi: 1.8 20.9877
言っとくけど、これ、おいらの身長/体重じゃないからね。で、中身はどうなってるか、観察 出来るようです。
> (Float methodNamed: #bmi: ) display Method bmi: text bmi: h ^ (self / h / h) literals Array ( #/ #/ ) bytecodes ByteArray 32 2 0 33 2 1 130 8 2 144 9 0 33 2 1 130 8 2 145 9 1 242 15 2 245 15 5 241 15 1 bmi:
何やら、バイトコードなんて言う、怪しいものが出てきましたな。で、いきなりmethodNamedなんて おいらが知らないのが出てきたけど、これはsuminさんからの受け売りです。どんな具合に定義されてるか 眺めてみます。定義は、basic.stにありました。
methodNamed: name (methods includesKey: name) ifTrue: [ ^ methods at: name ]. (superClass notNil) ifTrue: [ ^ superClass methodNamed: name ]. ^ nil
生半可な読解力によると、methodsにキーが登録されてたら、その値を返す。そうで なかったら、上のクラスで探してね。どんどん上へと辿っていって、どうしても見つからなかったら、nilを返してねって事だな。 そうすると、各メソッドはクラス毎に管理してんだな。
ついでに、displayも見とくと、同じファイルにあったよ。
display ('Method ', message) print. 'text' print. text print. 'literals' print. literals print. 'bytecodes' print. bytecodes class print. bytecodes do: [:x | (x printString, ' ', (x quo: 16), ' ', (x rem: 16)) print ]
どうも、これらしい。いえねぇ、displayって、オブジェクトによって態度を変えるんですよ。
> 33 display (Class Integer) 33 33 > 'hal' display $h $a $l hal > (1/5) display (Class Fraction) 1/5 1/5
上に出したdisplayは、メソッド用だな。で、メソッドの要素は、名前(message)、ソースコード (text)、定数?(literals)、VM用の命令列(bytecodes)。バイトコードは、そのもののバイトと、 それを4ビットのニブルに分解したのを表示してんな。
それにしても、同じdisplayってのが、collect.stとかtty.stとかにも定義してあって、わけわかめ な世界だなあ。smalltalkはちゃんと、それらの使い道を掌握してるからいいでしょうけど、それを 追う人は、無駄に脳味噌を使わされるな。ぼけ防止にはビッタリですってか。まあ、神経衰弱やってるようなものか。
全体はどうなってる?
上の例だと、ちまちましてたな。全体はどうなってるか? 上から下へと行ってみよう。
> classes Dictionary ( False Random LongInteger Dictionary Interval Boolean Object Link Fraction Collection List Switch Semaphore Context Scheduler Magnitude Float Method UndefinedObject Set Symbol ByteArray Block True File Number Char String Integer Array IndexedCollection Process Class Smalltalk )
これが、登録されてるクラスの全体像。そして、各クラスの中にはどんなメソッドが潜んで いるかと言うと、一例をば
> Random methods Dictionary ( set: next randInteger: next: between:and: ) > Smalltalk methods Dictionary ( saveImage: perform:withArguments:ifError: error: watch saveImage echo inquire: perform:withArguments: getPrompt: )
疲れたので、このぐらいにしとく。
差分表示
使う時は、下記をdiff.txtなんてファイルにコピーしてから、下記のようにしてください。
vi diff.txt cd little-st patch -p1 < ../diff.txt
diff -ur small-v3/Makefile little-st/Makefile --- small-v3/Makefile Sat Apr 8 02:05:47 1989 +++ little-st/Makefile Fri Apr 5 06:36:28 2013 @@ -1,4 +1,4 @@ -CFLAGS = -O +CFLAGS = -g # define groups of files, to make later commands easier INTERPc = memory.c names.c news.c interp.c diff -ur small-v3/initial.c little-st/initial.c --- small-v3/initial.c Sat Mar 4 04:16:36 1989 +++ little-st/initial.c Fri Apr 5 07:05:35 2013 @@ -8,6 +8,8 @@ # include "env.h" # include "memory.h" # include "names.h" +static goDoIt(text); +static makeInitialImage(); int initial = 1; /* making initial image */ diff -ur small-v3/parser.c little-st/parser.c --- small-v3/parser.c Sat Mar 4 04:16:37 1989 +++ little-st/parser.c Fri Apr 5 07:00:21 2013 @@ -60,6 +60,14 @@ static int instanceTop; static char *instanceName[instanceLimit]; +static parsePrimitive(); +static genMessage(toSuper, argumentCount, messagesym); +static expression(); +static assignment(name); +static body(); +static block(); + + static int maxTemporary; /* highest temporary see so far */ static char selector[80]; /* message selector */ diff -ur small-v3/primitive.c little-st/primitive.c --- small-v3/primitive.c Sat Apr 8 01:11:21 1989 +++ little-st/primitive.c Fri Apr 5 06:37:59 2013 @@ -49,7 +49,7 @@ extern int linkPointer; extern double frexp(), ldexp(); -extern long time(); +/*extern long time();*/ extern object ioPrimitive(INT X OBJP); extern object sysPrimitive(INT X OBJP);