最近、読んだ「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)