Archive for the ‘friend-of’ Tag
newLISP で On Lisp する...第12章(番外編)
前回の 第12章 汎変数(その1)から、いきなり、番外編です(笑)。
さて、前回見たように、newLISP では、参照を返すマクロは作れませんでした。つまり、setf の汎変数に自作のマクロを適用できないということ。そこで、以前それを可能にするマクロを考えました。
しかし、newLISP の進化はそれより早く、現在では、参照を返す記述が可能です。マクロではなく macro を使って。だから、番外編(笑)。
前回記述したcontext 関連の参照を返すマクロ friend-of
(define-macro (friend-of)
(letex (_p (args 0)
_q (args 1))
((*friends* _p) _q)))
を macro にします。
(module "macro.lsp")
(macro (friend-of P Q)
((*friends* P) Q))
あとは、前回使った toggle を用意して、
(define-macro (toggle)
(letex (_obj (args 0))
(setf _obj (not $it))))
データを定義します。
> (new Tree '*friends*) *friends* > (*friends* "marry" (new Tree 'marry.*friends*)) marry.*friends* > ((*friends* "marry") "john" true) true > ((*friends* "marry") "john") true > (setf ((*friends* "marry") "john" ) nil) nil > ((*friends* "marry") "john") nil > (toggle ((*friends* "marry") "john")) true > ((*friends* "marry") "john") true > (gethash "john" (gethash "marry" *friends*)) true >
ここまでは、前回と同じ。これで、動作確認の準備完了です。
macro の friend-of を試します。
> toggle (lambda-macro () (letex (_obj (args 0)) (setf _obj (not $it)))) > friend-of (lambda-macro (P Q) (expand '((*friends* P) Q))) > (toggle (friend-of "marry" "john")) nil > (friend-of "marry" "john") nil > (setf (friend-of "marry" "john") true) true > (friend-of "marry" "john") true >
といった感じで、違和感なく使えます(笑)。もちろん、macro ならではの制限もありますが(汗)、toggle と friend-of を直行的に使えます。(もちろん、setf とも)
newLISP では、macro を使って、プログラムを "見事にモジュール化され、なおかつ美しくエレガント" にできるというわけです。
これで、前回の問題点は、クリアされました。次回は再び、第12章 汎変数(その1)に戻ります。
以上、如何でしょうか?
newLISP で On Lisp する...第12章(その1)
(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsp に定義してあります。)
ついに、第12章 汎変数 まで来てしまいました。私にとっては、“On Lisp” の中でも、最も難解な章です。最も Lisp らしい章であるともいえます。
まずは、汎変数という概念 から。
Common Lisp の setf はマクロなので、macroexpand を使って展開式を確認できます。しかし、newLISP の組込を展開してみることはできません。つまり、Common Lisp のようにインバージョンな様子を見ることが出来ないのです。また、インバージョンが、表に出てこない上に、それらにまつわるマクロも用意されていません。つまり、自分で組むしかないということ。やりがいがあるともいえます(笑)。
インバージョンとは、言ってみれば、融通の利かないプログラミング言語に、「それくらい、気を利かせろ!」と、いっているようなもの。他のプログラミング言語(C++ とか、pascalとか)なら、せいぜい、warning を出すのが関の山。多くの場合、バグになります。だから、そもそも、そういう記述をしないのが、優秀なプログラマと見られるところ。しかし、Lisp では、違います。言うことを聞くように、Lisp を作り変えてしまうのが、優秀な Lisper? Lisp の持つ強力なマクロなしには、実現できないことでしょう。
さてさて、newLISP は、どこまで Common Lisp に迫れるか? 違った、私の実力は? ですね(笑)。
またまた、前置きが長くなりましたが、“On Lisp”本書の例から、newLISP での動作例を
> (setq lst '(a b c)) (a b c) > (setf (first lst) 480) 480 > lst (480 b c) >
ここで、(first lst) が 汎変数にあたるわけです。car に相当する first は、Common Lisp でも使えます。
(define-macro (toggle)
(letex (_obj (args 0))
(setf _obj (not _obj))))
の動作は、
> [cmd] (let ((lst '(a b c))) (toggle (first lst)) lst) [/cmd] (nil b c) >
ここまでは、本書と同じです。
次の例からは、hash が無いので、context で代用します。
> (define *friends*:*friends*) nil > (*friends* "marry" (new Tree 'marry.*friends*)) marry.*friends* > ((*friends* "marry") "john" true) true > ((*friends* "marry") "john") true > (setf ((*friends* "marry") "john" ) nil) nil > ((*friends* "marry") "john") nil > (toggle ((*friends* "marry") "john")) true > ((*friends* "marry") "john") true >
Tree は、context を hash 代わりに使う時の組込テンプレートです。友人 john の加わった marry.*friends* を使って、anne の友人を設定すると、
> (*friends* "anne" (new marry.*friends* 'anne.*frineds*)) anne.*frineds* > ((*friends* "marry") "john") true > ((*friends* "anne") "john") true >
上記のように、marry の友人が anne の友人にコピーされます。
そして、
(define-macro (friend-of)
(letex (_p (string (args 0))
_q (string (args 1)))
((*friends* _p) _q)))
を定義して、
> (friend-of "marry" "john") true > (friend-of "anne" "john") true >
となります。しかし、toggle と friend-of の直交性は、
> (toggle (friend-of "marry" "john")) nil > (friend-of "marry" "john") true >
ご覧の通り、期待できません。前にも書きましたが、Common Lispのマクロは、呼び出された場所で展開式が評価されますが、newLISPでは、マクロ内で評価されてしまうからだと考えられます(泣)。
問題点が見つかったところで、気を取り直し、複数回の評価に関わる問題 に入ります。
前述のマクロtoggle の動作は、
> toggle (lambda-macro () (letex (_obj (args 0)) (setf _obj (not _obj)))) > [cmd] (let ((lst '(true nil true)) (i -1)) (toggle (nth (++ i) lst)) lst) [/cmd] (true nil true) >
と、toggle されません。++ は、incf と同じ破壊的組込関数なので nth に置かれたインデックス変数 i が変わっていくためです。
Common Lisp には、define-modify-macro がありますが、newLISPにはありません。しかし、代わりに、$it があります。
(define-macro (toggle)
(letex (_obj (args 0))
(setf _obj (not $it))))
とすれば、
> toggle (lambda-macro () (letex (_obj (args 0)) (setf _obj (not $it)))) > [cmd] (let ((lst '(true nil true)) (i -1)) (toggle (nth (++ i) lst)) lst) [/cmd] (nil nil true) >
となります。$it は、汎変数の値を保持していますので、望ましい toggle のインバージョンを定義できるわけです。
さて、ちょっと早いですが、新しい ユーティリティ からは、次回以降に(汗)。
以上、如何でしょうか?