Archive for the ‘プログラミング’ Category

defmacro!

火曜日, 2月 17th, 2009

土曜になつたんさんとあろはさんとお会いして、飲んできました。
お二人とも楽しい話をありがとうございました。
今、なつたんさんにお借りしたLet Over Lambdaを読んでいるのですが、
噂どおり非常に濃い内容でした。
最初の方に登場するマクロでさえ恐ろしい。
Paul GrahamがOn Lispで書いたマクロnifが

(defmacro nif expr pos zero neg)
(let ((g (gensym))
`(let ((,g ,expr))
(cond ((plusp ,g) ,pos)
((zerop ,g) ,zero)
(t,neg)))))

Let Over LambdaのDoug hoyteの手によって、

(defmacro/g! nif (expr pos zero neg)
`(let ((,g!result ,expor))
(cond ((plusp ,g!result) ,pos)
((zerop ,g!result) ,zero)
(t ,neg))))

物凄い勢いで改造されていく。

(defmacro! nif (o!expr pos zero neg)
`(cond ((plusp ,g!expr) ,pos)
((zero ,g!expr) ,zero)
(t ,neg)))

なんてこった。
ネストがどんどん浅くなっていって、
どんどん訳が分からなくなっていく。
defmacro!はwith-gensymsとonly-onceを隠蔽したようなマクロで、
慣れたらかなり便利そうです。
(with-gensymsとonly-onceについてはそれぞれ”On Lisp“と”実践Common Lisp“を参照してください(笑))
この辺りはWebで無料で読めるので、
defmacro/g!やdefmacro!の定義が気になる方はこちらをどうぞ。

Lispは未来に生きている

火曜日, 2月 10th, 2009

Lispは常に未来を先取りしてきました。
GCがJavaやLL等により一般に浸透するよりも遥か昔からGCを備えていたし、
「クロージャ! クロージャ!」と騒がれる遥か昔よりクロージャを備えてました。
Lispは”現在”ではなく”未来”に生きているんです。
Lispが未来に生きていることは簡単なプログラムを書くことですぐに分かります。
もうすぐ、UNIX timeが「1234567890」になると騒がれていますが、
Lispの中ではそんな時間は既に通り過ぎているのです。

CL-USER> (decode-universal-time 1234567890)
30 ;
31 ;
8 ;
15 ;
2 ;
1939 ;
2 ;
NIL ;
-9

Common Lispの中での時間が「1234567890」を迎えたのは、
日本時間で1939年2月15日8時31分30秒です。
UNIX timeの「1234567890」が日本時間の2009年2月14日8時31分30秒ですから、
約70年も未来を行っているということになります。
ところで、Lispが出来たのって何年だっけ…
ちなみに、Common Lispでのuniversal time「3456789012」を
日本時間で2009年7月17日12時10分12秒に迎えます。
誰か祝ってあげてください。


これを書いてるときに気づいたけど、、Windows版CLISPでのencode-universal-timeの挙動がおかしい。
手元のFreeBSD版では問題なく動くコードが、

[1]> (encode-universal-time 0 0 0 1 1 1970)
2208956400

Windows版では動作しない上にプロセスが死んでしまう。

[1]> (encode-universal-time 0 0 0 1 1 1970)
*** - handle_fault error2 ! address = 0x0 not in [0x19d70000,0x19ef2efc) !
SIGSEGV cannot be cured. Fault address = 0x0.
GC count: 3
Space collected by GC: 0 1311528
Run time: 0 2103024
Real time: 0 197283680
GC time: 0 200288
Permanently allocated: 92384 bytes.
Currently in use: 2626228 bytes.
Free space: 385581 bytes.

CLISPバージョン2.45でこの現象を確認して、その後バージョンを最新の2.47に上げても同様の動作だった。
Windows版CLISPのバグ?

総称関数の先行実行

日曜日, 2月 8th, 2009

並列記号処理という本を読んだんですが、なかなか面白いです。
タイトルからはあまり想像が付きませんが、
高級言語マシンについての話がほとんどで、
Lispマシンなどが好きな人にはお勧めできます。
その中で一箇所印象に残ったものを取り上げます。
Symbolics 3600の後継マシンであるIvoryは、
総称関数の呼び出しの際に、高頻度で使用される命令を先行処理し、
それ以外の命令の実行が必要だった場合は、
割り込みによって処理を切り替えるそうです。
残念ながら、あまり詳しく書いてありませんでしたが、
次のような感じかと思います。

(defmethod m1 ((num number))  ;;m1-1
(1+ num))
(defmethod m1 ((numlis list))  ;;m1-2
(1+ (reduce #'+ numlis)))

上記のような総称関数があり(便宜上、m1-1, m1-2と呼び分けます)、
もし、m1-1(引数の型がnumber)の方が高頻度で呼ばれるならば、
m1が呼ばれた際に、型チェックに先行してm1-1を呼び出し、
後で、引数の型がリストだとわかったら、割り込みを用いて、
途中まで行った処理を無効化し、m1-2に処理を切り替えるという感じでしょうか。
これは、今時のCPUなら必ず付いている分岐予測の一種だとは思いますが、
『総称関数の呼び出し』という大きな単位についてこれが行われるのは
いかにも専用マシンっぽくてかっこいいですよね。

ESP – Prologでオブジェクト指向

月曜日, 2月 2nd, 2009

その昔、ESP(Extended Sequential Prolog)という
Prologにオブジェクト指向を加えたような言語があったそうです。
このESPはFlavorsを参考にして作られており、なかなか面白いです。
ESPについての資料第五世代コンピュータプロジェクト・アーカイブスにおいてあります。
T. Chikayama, “Unique Features of ESP”
ここから分かるように、ESPではクラスの多重継承が可能で、
beforeデモン、afterデモンなんてものがあります。

class with_a_lock has
instance
component lock is lock;
before:open(Obj) :- unlocked(Obj!lock);
...
end.
class door has
instance
component state := closed;
:open(Door) :- Door!state := open;
...
end.
class door_with_a_lock has
nature door, with_a_lock;
end.

クラスwith_a_lockは成分lockを持ち、その初期値値はlock(これはアトム)です。
また、beforeデモン述語openを持ち、これは引数のunlock(ここでは定義されていない)を行います。
クラスdoorは成分stateを持ち、その初期値はclosedです。
また、述語openを持ち、これは引数のlockスロットの値をopenに設定します。
クラスdoor_with_a_lockはdoorとwith_a_lockを継承しています。
ここで次のように、door_with_a_lockのインスタンスを作り、
openを呼ぶと、まずbeforeデモンによりwith_a_lockのopenが呼ばれ、
その後にdoorのopenが呼ばれるという仕組みです。

:new(#door_with_a_lock, DWL),
:open(DWL),

これをCommon LispでCLOSを使って書くと次のような感じでしょうか。

(defclass with_a_lock ()
((lock :accessor with_a_lock-lock
:initform 'lock)))
(defclass door ()
((state :accessor door-state
:initform 'closed)))
(defclass door_with_a_lock (with_a_lock door) ())
(defmethod my-open :before ((obj with_a_lock))
(format t "with_a_lock open~%")
(setf (with_a_lock-lock obj) 'unlock))
(defmethod my-open ((obj door))
(format t "door open~%")
(setf (door-state Obj) 'open))

実行してみると、with_a_lockのopenが呼ばれてからdoorのopenが呼ばれているのが分かります。

CL-USER> (setf *dwl* (make-instance 'door_with_a_lock))
#<DOOR_WITH_A_LOCK #x19FAE3A9>
CL-USER> (my-open *dwl*)
with_a_lock open
door open
OPEN

Flavorsが出来た時期を考えてみたら、
影響を受けた言語が沢山あってもおかしくはないのに、
なかなか見かけないのはなんでだろ…

Lisp本とか色々

金曜日, 1月 30th, 2009

*Lisp本*
そろそろ新しくLispの本が一冊くらい欲しいんですが、
Paradigms of Artificial Intelligence Programming

Let Over Lambda
で迷っています。
Let Over Lambdaの方が安いし読みやすそうでいいかなと思っているんですが、
日本語訳が出るとか出ないとかいう噂を聞いたので悩んでいます。
Programming Erlangの時みたいにがんばって全部読み終えた頃に日本語訳が出ると悲しい気分になれる…
*カッコイイformatの例*
CLのformatを使ったカッコイイ例って結構ありますよね。
時々「あの例ってどこに書いてあったっけ?」と探し回るので、
忘れないようにメモを書き残しておきます。
まずは、CLTL2より、Lisp Machineのformatの内部ルーチンであるformat-error。

(defun format-error (string &rest args)
(error nil "~?~%~V@T↓~%~3@T\"~A\"~%"
string args (+ *ctl-index* 3) *ctl-string*))

これはformatの引数に誤りがあったときに呼び出され、
次のように表示が行われます。

(format t "The item is a ~[Foo~;Bar~;Loser~]." 'quux)
>>ERROR: The argument to the FORMAT "~[" command
must be a number.
↓
"The item is a ~[Foo~;Bar~;Loser~]."

次に、bit 85年5月号の「Common Lisp入門」より、8クイーン問題の解の出力

(apply 'format t
"~@{~%~V{. ~}Q ~V{. ~}~}"
(mapcan #'(lambda (x)
(list x '( t ) (- 7 x) '( t )))
column))

これはcolumnが(3 1 6 2 5 7 4 0)の場合次のように出力されます。

. . . Q . . . .
. Q . . . . . .
. . . . . . Q .
. . Q . . . . .
. . . . . Q . .
. . . . . . . Q
. . . . Q . . .
Q . . . . . . .

どちらもかっこよすぎ。
しかし、読む人が大変だwww
*Syntax Salt*
とある方に、CLでの中置記法の記事[pdf]を読んで頂いたときに行われた会話。
「てっきりSyntax Sugarとか構文糖とかいう言葉が出てくるものだと思ったら出てこないんだね」
『これは本来読みやすいS式を読みにくい中置記法にしてるからSyntax Sugarじゃないんですよ』
「なるほど。じゃあ構文塩なんやね」
構文塩…?
構文糖、糖衣構文、Syntax Sugarにそれぞれ反意語があるとすれば、
それらは構文塩、塩漬構文、Syntax Saltあたりになるんでしょうか。
これら言葉が実在するのか気になったので検索してみました。
そしたらSyntax Saltでひっかかった。

Syntax Salt
古いコンピュータ言語しか知らない中年管理職に売り込む目的で、新しいコンピュータ言語の構文を退化させ、一見古いコンピュータ言語のように見せること。[反対語 = Syntax Sugar]
「例」
オブジェクト指向COBOL
Syntax Saltを振りかけ、本来 A move: Bと書くべきところを move A to Bと書くことを許し、価格を釣り上げた、日経スポーツ愛読者の管理職向けSmalltalkのこと。
(佐原伸 私家版悪魔の辞典より)

とりあえず、ネタ用語としては存在するようです。
糖衣構文という言い方が好きな私としては塩漬構文という言葉を推したい。

ポケステでLispインタプリタ動かしてみた

火曜日, 1月 27th, 2009

前から作っていたポケステ用Lispインタプリタですが、ついに実機で動作しました。

実機ではネストの深いリストを作ると動作がおかしくなってしまいました。
(だから上の動画では簡単な式しか試していません(笑))
関数の再帰呼び出しでスタックが破壊されているんじゃないのかと疑っていますが、
今のところ原因はまだ分かっていません。
でもまあとりあえず動いたので満足してます。

続・Lispマシンあれこれ

日曜日, 1月 25th, 2009

『LISPマシン・プログラミング技法LISPマシン・プログラミング技法』ですが、
ようやくほとんど読み終えました。何とか返却期限までに全部読めそうです。
忘れないうちに気になったことをメモ。
*stack-let*
Lispマシンは効率に関してかなり気を使っているらしく、色々な工夫がしてあるそうです。
その中の一つにstack-letというマクロがあり、これを使うと変数をヒープではなく、
スタックに割り付けることが出来るそうです。
現在のCommon Lispではdynamic-extent宣言でしょうか。
マクロを使ってこんな風にかけるかと思います。

(defmacro stack-let (bind &body body)
`(let ,bind
(declare (dynamic-extent
,@(mapcar
#'(lambda (b) (if (consp b) (car b) b))
bind)))
,@body))

*エリア*
ページフォルトの回数を減らすために、エリアというものがあるそうです。
オブジェクト(flavorに限らず配列やコンスも)を作成する際に、エリアというものを指定でき、
同一のエリアを指定すれば(可能な限り)同一のページに配置されるそうです。
もちろん新たなエリアを自分で定義することも可能です。

(defvar *my-area* (make-area :name '*my-area*))
(defun create-a-foo (&rest options)
(apply #'make-instance 'foo :area *my-area* options))

このようにしておくと、create-a-fooで作られたインスタンスは
同一のページに配置される(可能性が高い)そうです。
*IP-TCP*
非常にどうでもいい話なんですが、IP-TCPという用語が出てきたんですよ。
今までTCP/IPという表記しか見たことがなかったんですが、
よくよく考えてみたらIPが先でも悪いことはないはずですよね。
どうしてTCPの方が先に書かれるようになったんだろう…

続・ポケステでLisp

木曜日, 1月 22nd, 2009

ねんがんの かきこみきをてにいれたぞ!

Lispマシンあれこれ

火曜日, 1月 20th, 2009

『Lispマシン・プログラミング技法』を読んで色々気になったことをメモ。
*ファイルの先頭*
サンプルプログラムのソースに、defpackageは出てくるものの、
in-packageが出てこないので、不思議に思っていたんですが、
第29回慢性的CL勉強会でg000001さんに

Lispマシンでは、ファイルの先頭の行が意味を持つので、
ここでパッケージを指定したりする。

と教えていただきました。
確かに、ソースの先頭の行を見てみると

;;; -*- Mode: LiSP; Syntax: Common-lisp; Package: GRAPHER; Base: 10 -*-

と、いかにも意味ありげなコメントが書いていました。
Lispマシンは基本的に8進数を使うという話を聞いたことがありますが、
上記の様に「Base: 10」を記述しておけば10進数を使えるみたいですね。
*CDR’ing down*
気になったソースとコメント。

(defmethod (delete-self node) ()
;; (borrowed from tv:sheet's :kill method)
;; Do it this way to prevent CDR'ing down list structure
;; being modified
(loop until (null arcs)
do (delete-self (car arcs)))
(setq *all-the-nodes* (delete self *all-the-nodes))

CARを取ってますが、その要素が削除されるためリストを先頭から辿っていくことになります。
当たり前といえば当たり前ですが、LISPではこんなソースを書いたことがなかったので、少々面食らいました。
ところで、”CDR down”という言葉は知っていたのですが、
“CDR’ing down”という言葉ははじめて知りました。
「くだりんぐ」の辺りがリストを下っていく感じがしていいですね。

*パッケージの指定*
現在のCommon Lispでは『cl:car』のようにコロンを使ってシンボルが属すパッケージを
指定することが出来ますが、当時のLisp Machineではもう少し凄いことが出来たようです。

(defparameter
*grapher0display-margin-components*
'dw:((margin-ragged-borders )
(margin-scroll-bar :history-noun "display")
(margin-scroll-bar :history-noun "display"
:margin :bottom)
(margin-white-borders :thickness 2)))

リストに対してパッケージを指定しています。詳しい説明は書いてありませんでしたが、
恐らくリスト内のシンボルが属すパッケージを丸ごと指定しているようです。
*入力できない*
ちょっと笑ったのがこれ。

(format t "~&Didn't find an arc between~
~'I⊂~A~⊃ ~and ~'I⊂~A~⊃"
node1 node2)

入力できないよ( ^ω^)
「~⊂」と「~⊃」フォントの変更を行うそうです。
引数に’Iが書いてあるので恐らくイタリック体に変更しているのかと思います。
*フォント*
上に続いてフォントの話。
「(char= char #\a)」という式について以下のような説明がありました。

char=による比較は厳密である。これは(大文字の)Aに対してtを返さないばかりか、
小文字のAに対しても、ファイルの中で異なった文字スタイルをしていたらtを返さない

そういえばCLTL1では文字型がfontという属性を持っていましたね。
Lispマシンの名残だったのか。
(「ファイルの中で」と書いてあるのはファイルから読み取った文字に対してchar=を使っているためです。)
他にも「引数の名前をignoreにしたら、その引数を使用しなくても警告が出ない」とか、
「declareは無いものの、ほぼそっくりの書き方で型宣言が出来る」など、
色々と気になる話がありました。やはりこの本は面白い。

Flavor

木曜日, 1月 15th, 2009

FlavorとはZetaLispで取り入れられたオブジェクトシステムで、
Lisp MachineでもFlavorが使われていたそうです。
私がFlavorというものをはじめてみたのは「bit 1985年 11月号」の
『Lispマシンのオブジェクト指向プログラミング』
という特集で、そこにはこんな感じのプログラムが書かれていました。

;;; 全部書くのが面倒だったので結構書き換えてます
(defflavor circle (x y r) ())
(defmethod (circle :define) (xi yi ri)
(setq x xi y yi r ri))
(setq c1 (make-instance 'circle))
(send c1 ':define 5 5 10)

メソッドを呼ぶのにsendという関数を使う必要があり、良くも悪くも
「オブジェクトにメッセージを送る」
というのがはっきりしてプログラムを書くものだったんですが、
『Lispマシン・プログラミング技法』
によるとこれは”古い”スタイルらしく(*1)
後に次のように書けるようになったそうです(*2)

(defflavor circle (x y r) ())
(defmethod (define circle) (xi yi ri)
(setq x xi y yi r ri))
(setq c1 (make-instance 'circle))
(define c1 5 5 10)

sendがなくなり、メソッドが通常の関数と同様に扱えるようになっています。
現在のCommon LispのオブジェクトシステムであるCLOSと同様ですね。

(*1) 新しいスタイルで書けるようになったのはGenera7.0がリリースされた1986年からのようです。
しかし、今となってはこれまた十分に古いですね(笑)
(*2) このようにインスタンス変数を設定するだけであれば、
defflavorのオプションである:initable-instance-variablesを使うことにより、
make-instanceの引数としてインスタンス変数の初期値を設定できます。
これが”古い”Flavorで出来たかどうかは分かりません。

このFlavor、かなり凶悪なもので、sendを使っていた”古い”スタイルの時から既に、
:beforeメソッド、:afterメソッドが定義でき、
:list, :and, :orなどといったメソッド結合も出来たそうです。
メソッドの第一引数がインスタンスに固定されていて、
引数(とそのマッチング)の自由度が低いことを除けば、
今のCLOSと大差がないように見えます。
Flavor恐ろしや・・・