#:g1: let-by-needの紹介

Posted 2014-12-09 15:00:00 GMT

(LISP Library 365参加エントリ)
(Lisp Advent Calendar 2014参加エントリ)

 LISP Library 365 の344日目、Lisp Advent Calendar 2014の10日目です。

let-by-needとはなにか

 let-by-needは、Martin Abadi氏作のマクロによる式変形で遅延評価を実現するものです。

パッケージ情報

パッケージ名let-by-need
ソースコードLETBYN.LSP[COM,LSP]-www.SailDart.org

インストール方法

 上記ソースコードをダウンロードして適当に動かします。
MacLISP用のようですが、Common Lispでもstatusを定義してやればそのまま動きます(statusを削るのも可)

(defmacro status (thing &rest args)
  `(case ',thing
     (feature (member (car ',args) *features*))
     (otherwise nil)))

試してみる

 saildartのCommon Lispのディレクトリを眺めていて見付けたのですが、割合に面白そうなので紹介してみることにしました。1982年のコードなので32年前のコードですね。
どんなものかは動作をみるとわかりやすいのですが、

(let-by-need ((a (print 'a))
              (b (print nil))
              (c (print 'c)))
  (and a b c))
;>>  
;>>  A 
;>>  NIL 
;=>  NIL

こんな感じに遅延評価的にletの束縛部のcは使われていないので評価されないというものです。

 マクロの種類としては、構文の乗っ取り型でボディの中身を書き換えてしまいます。

(let-by-need ((a t)
              (b nil)
              (c t))
  (if (or a b)
      (and a c)
      (if a b c)))

;==> (LET ((A T)) (COND (A (AND A (LET ((C T)) C))) (T (LET ((B NIL)) (COND (B (AND A (LET ((C T)) C))) (T (AND T (COND (A B) (T (AND T (LET ((C T)) C)))))))))))

 let-by-needが把握できる式変形は、関数呼び出し、lambda式、if、cond、and、orです。

(let-by-need ((a (print 'a))
              (b (print nil))
              (c (print 'c)))
  (if a c b))
;>>  
;>>  A 
;>>  C 
;=>  C

;===>
(LET ((A (PRINT 'A)))
  (COND
   (A
    (LET ((C (PRINT 'C)))
      C))
   (T
    (AND T
         (LET ((B (PRINT NIL)))
           B)))))

(let-by-need ((a (print 'a))(b nil)(c (print 'c))) (lambda () a)) ;=> #<FUNCTION (LAMBDA ()) {1016F712AB}> ;===> (LAMBDA () (LET ((A (PRINT 'A))) A))

(let-by-need ((a (print 'a)) (b (print nil)) (c (print 'c))) (list a (list c))) ;>> ;>> A ;>> C ;=> (A (C)) ;===> (LET ((A (PRINT 'A)) (C (PRINT 'C))) (LIST A (LIST C)))

まとめ

 今回は、let-by-needを紹介してみました。
遅延評価のために元の式の方をマクロで書き換えてしまうというのはありそうでない方式ですね。
明日のLisp Advent Calendar 2014は、@y2q_actionmanさんです。お楽しみに。

comments powered by Disqus