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 のインバージョンを定義できるわけです。

 さて、ちょっと早いですが、新しい ユーティリティ からは、次回以降に(汗)。

 以上、如何でしょうか?