reader macro その3

On Lispに書かれていたデリミタの例だが,

(set-macro-character #\] (get-macro-character #\)))

(set-dispatch-macro-character #\# #\[
  #'(lambda (stream char1 char2)
      (let ((accum nil)
            (pair (read-delimited-list #\] stream t)))
        (do ((i (ceiling (car pair)) (1+ i)))
          ((> i (floor (cadr pair)))
           (list 'quote (nreverse accum)))
          (push i accum)))))

> #[2 7]
(2 3 4 5 6 7)

例えばこのようにするとエラーになってしまう.

(let ((start 2)
      (end 7))
  #[start end])

なので次のようにして使うことにした.

(defun make-number-list (start end)
  (nlet iter ((i start)
	      (product nil))
	(if (> i end)
	    product
	    (iter (1+ i) (append1 product i)))))

(set-macro-character #\] (get-macro-character #\)))
(set-dispatch-macro-character #\# #\[
  #'(lambda (stream char1 char2)
      (let ((pair (cons 'list (read-delimited-list #\] stream t))))
	`(apply #'make-number-list ,pair))))

> (let ((start 2)
        (end 7))
    #[start end])
(2 3 4 5 6 7)

なんか楽しくなってきた!これでエラトステネスのふるいなど書いてみる.

(defun prime (n)
  (nlet iter ((prime-list #[2 n])
	      (m 2))
	(if (= m n)
	    prime-list
	    (iter (remove-if #'(lambda (x)
				 (and (not (= x m))
				      (zerop (mod x m))))
			     prime-list)
		  (1+ m)))))

> (prime 100)
(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97)

nletはSchemeのnamed-letです。繰り返しに再帰を使うのがより自然であるように思うようになってきた。Common LispSchemeと違って言語仕様として末尾最適化が要求されているわけではないものの、SBCLは末尾最適化をやっているそうなので心おきなく使える。xyzzyとかはどうなんだろう。