Archive for the ‘defstruct’ Tag

newLISP で On Lisp する...第15章(その4)

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

 第15章 関数を返すマクロ の最後は、遅延評価 です。
 newLISP にも、Common Lisp 同様、遅延評価は組み込まれていません。“On Lisp”本書では、CommonLisp の構造体を使って実装しています。しかし、newLISP には、構造体もありません。
 ですから、前の時は、構造体を使わずにマクロを組みました。実際、現在の newLISP でもそのまま動きます。
 そのまま掲載したも良かったのですが、その後、newLISP で構造体を定義したので、今回はそれを使ってみます。

 まずは、newLISP の構造体のおさらいから、(gensym、mklist、reference-inversionマクロ群は、newlisp-utility.lsp に定義してあります)

(define-macro (structfunc funcname propname)
  (letex (_funcname funcname
          _propname propname)
    (setq _funcname (lambda (symbol)
      (letex (_sym (sym '_propname symbol))
        (reference-inversion:set _sym))))))

(define-macro (structfuncs)
  (letex (_pair (cons 'begin (map (curry cons 'structfunc) (args))))
    _pair))

(define-macro (defstruct defname)
  (let (_name defname
        _var (map (fn (x) (first (mklist x))) (args))
        _val (map (fn (x) (second (mklist x))) (args)))
    (letex (_defctx _name
            _strucp (sym (string _name "-p"))
            _copy-n (sym (string "copy-" _name))
            _make-n (sym (string "make-" _name))
            _funcs (cons 'structfuncs (map (fn (x) (list (sym (string _name "-" x)) x)) _var))
            _vari _var
            _vali _val)
      _funcs
      (setq _copy-n (fn (s) (letex (_ctx (sym (gensym))) (new s '_ctx))))
      (setq _strucp (fn (s) (and (structurep s) (= (s 1) '_defctx))))
      (setq _make-n
            (lambda-macro ()
              (let (_gsym (sym (gensym)) 
                    _vars (append '_vari (map (fn (x) (first (mklist x))) (args)))
                    _vals (append '_vali (map (fn (x) (second (mklist x))) (args))))
                (letex (_ctx _gsym 
                        _default (sym _gsym _gsym)
                        _structurep (append (list 'structure '_defctx) '_vari)
                        _var (cons 'setq (apply append (transpose (list (map (hayashi sym _gsym) _vars) _vals))))) 
                        ; 2010/ 8/26 corrected
                        _var (cons 'setq (flat (transpose (list (map (hayashi sym _gsym) _vars) _vals)))))
                  (setq _default '_structurep)
                  _var 
                  _ctx)))))))

(defun structurep (s)
  (letex (_s (sym (string s) s))
    (and (context? s) _s (= (s 0) 'structure))))

 二種類あるうち、context を使った実装の方です。
 マクロdefstruct を使って、

(defstruct delay forced closure)

 とすれば、コンストラクタmake-delay を使って、delay構造体を作ることができます。
 同時に、アクセス関数、述語、コピー関数も実装されます。
 構造体の用意ができたところで、遅延オブジェクトを実装します。

(define unforced (gensym))

(defstruct delay forced closure)

(defun force ()
  (if (delay-p (args 0))
      (if (= (delay-forced (args 0)) unforced)
          ((delay-closure (args 0)))
        (delay-forced (args 0)))
    (args 0)))

(define-macro (delay)
  (let (_self (gensym))
    (letex (_expr (args 0))
      (setq _self (make-delay (forced unforced)))
      (letex (_sym _self)
        (reference-inversion:setf (delay-closure _self)
                                  (fn () (reference-inversion:setf (delay-forced _sym) _expr))))
      _self)))

 setf の代わりに reference-inversion:setf を使っているのがミソ(笑)。
 動作は、こんな具合です。

> (setq x 2)
2
> (setq d (delay (begin (println "forecd.") (inc x))))
gensym3
> x
2
> (delay-p d)
true
> (force 'a)
a
> (force d)
forecd.
3
> x
3
> (force d)
3
> x
3
> 

 このように、force で最初に呼ばれた時に、評価され、その後は、評価された値が返ります。
 動作としては、合っていると思います。
 しかし、newLISP は、一定の条件下では、レキシカル・クロージャですが、基本的には、ダイナミック・クロージャです。本書の例のようには動きません。 (i+ は、newlisp-utility.lsp に定義してあります。)

> (let ((y 2)) (setq d2 (delay (i+ y))))
gensym5
> (force d2)

ERR: value expected in function + : MAIN:y
called from user defined function reference-inversion:reference-inversion
called from user defined function reference-inversion:setf
called from user defined function force
> 

 何故か? y が定義されていないからです。レキシカル・クロージャなら問題ないのですが、ダイナミック・クロージャの newLISP では force 時に y が見えなくなっています。
 ですから、トップレベルで y を定義してやれば、

> y
nil
> (setq y 0)
0
> (force d2)
1
> y
0
> (setq y 2)
2
> (force d2)
1
> 

 と風に動作します。
 これは、実装の問題ではありません。仕様です(汗)。
 ですが、下記のように、

> (setq d3 (delay (begin (let (x 2) (println "forced.") (i+ x)))))
gensym7
> x
20
> (force d3)
forced.
3
> x
20
> (force d3)
3
> 

 変数x を delay 内に記述すれば、本書の例のようにレキシカルに扱えます。“On Lisp”本書の例の意図にあっているかどうかはわかりません、y が let 文内でどう使われるか次第です(汗)。

 さて、第15章 関数を返すマクロ のまとめです。

  • newLISP でも、関数を返すマクロは作れます。

 当たり前ですね(笑)。

 以上、如何でしょうか?

広告