読者です 読者をやめる 読者になる 読者になる

デバッグプリント補助マクロ

common lisp read macro

僕が行うデバッグ(及びプログラムの動作確認)のほとんどは、プリントデバッグだ。
common lispのprint系の出力関数は、引数の値を出力した後(?)そのまま返してくれるので、ある式の値を手軽に知りたいときは、その式を(print exp)といったように出力関数で囲むだけでいい。

(defun add (a b)
  (+ (print a)   ;; aの値は?
      b))

> (add 5 20)
5
--> 25

ただ、この方法だと、値を知りたい式がころころ変わる場合(デバッグの際には珍しくない)に、print関数のつけはずし-特に閉じ括弧の除去-が面倒だったりする。
なので、そのためのリードマクロを作成してみた。

;; #D expression
;; #nD printed-expression_1 ... printed-expression_n  main-expression
(set-dispatch-macro-character #\# #\d
  (lambda (stream ch n)
    (declare (ignore ch))
    `(progn (format t "~&~@{~A~}" ,@(loop repeat (1- (or n 1)) collect (read stream t nil t)))
            (prin1 ,(read stream t nil t)))))

次のように使う。

;;
(defun x*y/3 (x y)
  (/ #D (* x y)  ;; x*yの値は?
     3))

> (y*x/3 7 9)
63
--> 21

;;
(defun x*y/3 (x y)
  (/ #1D"X*Y: "(* x y)  ;; x*yの値は?
     3))

> (y*x/3 7 9)
X*Y: 63
--> 21

簡略化(2009/10/07)

もっと簡単に使えるものも作成。

;; ? exp
;; ? label-string exp
(set-macro-character #\?
  (lambda (stream ch)
    (declare (ignore ch))
    (let ((exp #1=(read stream t nil t)))
      (if (stringp exp)
          `(progn (format t "~&~A" ,exp) (prin1 ,#1#))
        `(progn (fresh-line) (prin1 ,exp))))))

> ?(+ 1 1)
2
2

> ?"label: "(+ 1 1)
label: 2
2