Archive for 2010年8月月20日|Daily archive page

newLISP で On Lisp する...第20章(その2)

(この blog は、“short short story または 晴耕雨読な日々”からの引越してきたもの。スクリプトは、newLISP V10.2.1 以降で動作するように書き直しています。
defun 等の newLISP組込関数に無い関数は、特に断らない限り、newlisp-utility.lsponnewlisp.lsp に定義してあります。)

 第20章 継続 の2回目。 いよいよ、今回から、継続渡しマクロ です。
(values は、 onnewlisp.lsp に定義してあります)

(setq *cont* values)

(define-macro (=lambda)
; (=lambda parms &body body)
  (letex (_parms (cons '*cont* (args 0))
          _body (cons 'begin (1 (args))))
    (fn _parms _body)))

(define-macro (=defun)
; (=defun name parms &body body)
  (let (_f (sym (string "=" (args 0))))
    (letex (_mname (cons (args 0)
                   (map sym (map (curry string "_") (args 1))))
            _fname (append (list _f '*cont*)
                           (map sym (map (curry string "_") (args 1))))
            _vars (if (args 1)
                      (transpose (list (args 1)
                                       (map sym
                                            (map (curry string "_")
                                                 (args 1)))))
                    '())
            _body (cons 'begin (2 (args))))
      (begin
        (define-macro _mname
           _fname)
        (define _fname
          (letex _vars _body))))))

(define-macro (=bind)
; (=bind parms expr &body body)
  (letex (_parms (args 0)
          _expr (args 1)
          _body (cons 'begin
                      (or (set-ref '=values (2 (args)) (eval *cont*))
                          (2 (args)))))
    (let (*cont* (fn _parms _body)) _expr)))

(define-macro (=values)
; (=value &rest retvals)
  (letex (_body (cons '*cont* (args)))
    _body))

(define-macro (=funcall)
; (=fancall fn &rest args)
  (letex (_body (append (list (args 0) '*cont*) (1 (args))))
    _body))

(define-macro (=apply)
; (=apply fn &rest args)
  (letex (_body (append (list 'apply (args 0) '*cont*) (1 (args))))
    _body))

 先ずは、マクロ=defun の展開式を、例によって、見てみましょう。

> (=defun add1 (x) (=values (+ 1 x)))
(begin 
 (define-macro (add1 _x) 
  (=add1 *cont* _x)) 
 (define (=add1 *cont* _x) 
  (letex ((x _x)) 
   (begin 
    (=values (+ 1 x))))))
> 

 展開式中の関数定義で letex を使って引数を渡しているのがポイントです。そのために、“On Lisp” 本書のコードより、複雑なマクロになっています(汗)。
 では、実際の動作を、

> (=defun add1 (x) (=values (+ 1 x)))
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=values (+ 1 x)))))
> (=defun bar (x) (=values (list 'a (add1 x))))
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=values (list 'a (add1 x))))))
> (bar 5)
(a 6)
> 

 “On Lisp”本書の実装例では、グローバル変数 *cont* の初期値に values が使われています。 newLISPには多値がないので、以前は list で代用していましたが、今回は、マクロvalues を使っています。そのため、マクロbar の戻り値は、“On Lisp”本書の例と同じです。

> (=defun message () (=values 'hello 'there))
(lambda (*cont*) 
 (letex () 
  (begin 
   (=values 'hello 'there))))
> (=defun baz () (=bind (m n) (message) (=values (list m n))))
(lambda (*cont*) 
 (letex () 
  (begin 
   (=bind (m n) (message) (=values (list m n))))))
> (baz)
(hello there)
> 

 マクロ=bind の動きも、予定通りです。というより、そうするために余計なコードをマクロ=bind に追加しています(汗)。
 “On Lisp”本書の解説にあるクロージャの連鎖の簡単な例は、次回に実装と解説をします。
 次に、マクロ =lambda と=funcall の使用例を、

> [cmd]
(let (f (=lambda (n) (add1 n)))
   (=bind (y) (=funcall f 9)
     (println "9 + 1 = " y) nil))
[/cmd]
9 + 1 = 10
nil
> 

 この辺りのマクロは、“On Lisp”本書のコード通りといったところです。
 “On Lisp”本書の 継続渡しマクロに付随する制限 は、同じように適用されます。

> [cmd]
(=defun foo (x)
  (=bind (y) (bar x)
    (println "Ho ")
    (=bind (z) (baz)
      (println "Hum.")
      (list x y z))))
[/cmd]
(lambda (*cont* _x) 
 (letex ((x _x)) 
  (begin 
   (=bind (y) (bar x) (println "Ho ") (=bind (z) (baz) (println "Hum.") (list x y 
      z))))))
> (foo 1)
Ho 
Hum.
Ho 
Hum.
(1 (a (1 2 (hello there))) (hello there))
> 

 ここまでは、うまく継続渡しがされています。
 しかし、今回紹介した newLISP の継続渡しマクロでは、更に制限があります。それは、継続渡しマクロを使ったツリーの探索 と併せて、次回に。

 以上、如何でしょうか?

広告