最近、読んだ「Land of Lisp」の中に遅延プログラミングがでてきた。
ポール・グラハムの「On Lisp」にもあったよなぁ1、とか、考えていてふと気づいた。レキシカルスコープがサポートされたんだからelispでも可能なんじゃね?
試してみた2。
;; -*- lexical-binding: t; -*-
(defmacro lazy(body)
(let ((forced (gensym))
(value (gensym)))
`(let ((,forced nil)
(,value nil))
(lambda()
(unless ,forced
(setf ,value (progn ,body))
(setf ,forced t))
,value))))
(defun force(lazy-value)
(funcall lazy-value))
(defmacro lazy-cons(a d)
`(lazy (cons ,a ,d)))
(defun lazy-car(x)
(car (force x)))
(defun lazy-cdr(x)
(cdr (force x)))
(defun lazy-nil()
(lazy nil))
(defun lazy-null(x)
(null (force x)))
(defun make-lazy(lst)
(lazy (when lst
(cons (car lst)(make-lazy (cdr lst))))))
(defun take(n lst)
(unless (or (zerop n)(lazy-null lst))
(cons (lazy-car lst)(take (1- n)(lazy-cdr lst)))))
(defun take-all(lst)
(unless (lazy-null lst)
(cons (lazy-car lst)(take-all (lazy-cdr lst)))))
いちおう動いているみたいだ。
注意。「lazy」はレキシカルスコープの環境で動かすこと。考えてみれば、当たり前なんだけど、最初、うまく動かなくて考えこんでしまった。lazyの定義はレキシカルスコープでやって、実行をダイナミックスコープ環境でやっていたので。closureになってなかった。
それにしてもcl.elでlabelsも使えるんだ。知らんかったわ。
;; -*- lexical-binding: t; -*-
(setq wrk (labels ((f (n)
(lazy-cons n (f (1+ n)))))
(f 1)))
(take 10 wrk)