2018年1月7日日曜日

遅延プログラミング

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

Footnotes:

1

確認してみたら3ページほどの記述だった。

2

マクロがそのまま、使えなかったので適当に変更した。