let-double

マクロの練習その2。Common Lisp では declare を使って変数の型を指定できる。コンパイラはより効率的なコードを生成してくれるので数値計算には便利。

(let ((a 2d0)
      (b 3d0))
  (declare (double-float a b))
  (let ((c (+ a b)))
    (declare (double-float c))
    (expt c 2d0)))

ただ、数値計算を想定した場合、ある結果を求める過程において let を使った変数の束縛が何層にもなる場合がある。その度に declare を指定するのは面倒だし見栄えもよろしくない。ということでマクロを使ってみる。

(defmacro let-double (bindings &body body)
  `(let (,@bindings)
     (declare (double-float
	       ,@(loop for bind in bindings
		    collect (car bind))))
     ,@body))

bindings には (変数 初期値) のリストが入るので、それぞれの変数 (car bind) を展開して double-float を指定している。このマクロを使うと先ほどのコードはより簡潔になる。

(let-double ((a 2d0)
	     (b 3d0))
  (let-double ((c (+ a b)))
    (expt c 2d0)))

macroexpandして確認してみる。

(LET ((A 2.0d0) (B 3.0d0))
  (DECLARE (DOUBLE-FLOAT A B))
  (LET ((C (+ A B)))
    (DECLARE (DOUBLE-FLOAT C))
    (EXPT C 2.0d0)))

ちゃんと展開されているようだ。あまり使いどころのなさそうなマクロだけど。

ちなみにさっき書いたコードの (do-lines ... ) の展開は、

(DO ((LINE (READ-LINE IN NIL 'EOF) (READ-LINE IN NIL 'EOF)))
    ((EQL LINE 'EOF))
  (FORMAT T "~S~%" LINE))

になる。