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))
になる。