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

by shigemk2

当面は技術的なことしか書かない

評価回数と変数補足

Emacs Lisp
(defmacro for-wrong1 (var start end &rest body)
  (declare (indent 3))
  `(let ((,var ,start))
     (while (<= ,var ,end)
       ,@body
       (setq ,var (1+ ,var)))))
;; 123nil
;; でもendが関数だったら、副作用も一緒に繰り返される
;; そして評価回数は4回となる。
(for-wrong1 i 1 3
  (princ i))		; => nil
;; 333nil
(for-wrong1 i 1 3
  (princ 3))		; => nil
;; 3333333nil
(for-wrong1 i 1 (princ 3)
  (princ 3))		; => nil
;; 3333nil
(for-wrong1 i 1 (princ 3) nil)		; => nil

;; 変数捕捉の問題が残ったマクロ
;; もしマクロ定義の外でlimitが定義されていたら
;; 望まれない結果となる
(defmacro for-wrong2 (var start end &rest body)
  (declare (indent 3))
  `(let ((,var ,start)
	 (limit ,end))
     (while (<= ,var limit)
       ,@body
       (setq ,var (1+ ,var)))))
;; 結果は3nil
(for-wrong2 i 1 (princ 3) nil)		; => nil

;; マクロの外で定義したlimit=7は
;; マクロの中のlimit=3に喰われた
(let ((limit 7) ret)
  (for-wrong2 i 1 3
    (setq ret limit))
  ret)					; => 3

;; とどのつまり、マクロの外で定義しえない
;; 変数を定義すればよいわけだが、
;; ムチャクチャな名前の変数を定義するのはエレガントじゃないので
;; gensym関数を使って絶対他とはかぶらないシンボルを作成する
(defmacro for (var start end &rest body)
  (declare (indent 3))
  (let ((limit (gensym)))
    `(let ((,var ,start)
	   (,limit ,end))
       (while (<= ,var ,limit)
	 ,@body
	 (setq ,var (1+ ,var))))))
;; でもまあloopがあるので特別ループをマクロ定義する必要はない

;; 123nil
(for i 1 3
  (princ i))		; => nil
;; 333nil
(for i 1 3
  (princ 3))		; => nil
;; 3333333nil
(for i 1 (princ 3)
  (princ 3))		; => nil
;; 3nil
(for i 1 (princ 3) nil)		; => nil

P183

Emacs Lispテクニックバイブル

Emacs Lispテクニックバイブル