Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2007-10-31

Common Idioms (2)

| 23:10 | Common Idioms (2) - わだばLisperになる を含むブックマーク はてなブックマーク - Common Idioms (2) - わだばLisperになる

引き続きBrian Mastenbrook氏のCommon Idiomsを拾い読みしてみることの2回目

お題

map1

暗記で再現

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defmacro map1 (function list &rest constants)
    (let ((list-const (gensym)))
      `(mapcar (lambda (,l) (funcall ,function ,l ,@constants))
	       ,list))))

できた。mapcarの拡張版といった感じ。

eval-whenで囲まれているのはmacroexpandの動作のためなんでしょうか。

その辺があまり理解できていない。

;; 動作
(map1 #'cons '(1 2 3 4) (gensym))
;=> ((1 . #:G2935) (2 . #:G2936) (3 . #:G2937) (4 . #:G2938)) 

お題

aif

暗記で再現:間違い

(defmacro aif (pred conseq else)
  `(let ((it ,pred))
     (if it
	 ,conseq
	 (macrolet ((set-it (val) `(setf it ,val)))
	   ,else))))

;; 正解
(defmacro aif (test conseq &optional (else nil))
  `(let ((it ,test))
     (declare (ignorable it))
     (if it ,conseq
         (macrolet ((setf-it (val) (list 'setf ',test val)))
           ,else))))

大分間違えた。普通のaifかと思っていたら、else節に追加機能がついていた。setf-itというマクロでitに代入できたりする。

そのsetf-itのところで間違えた。それと、declare忘れ、else節をoptionalにするのを忘れたり。

;; 動作
(defparameter *special* '(nil hello))
(aif (car *special*) it (setf-it 42))
*special*
;=> '(42 hello)

お題

with-gensyms

暗記で再現

(defmacro with-gensyms ((&rest vars) &body body)
  `(let ,(map1 #'list vars '(gensym))
     ,@body))

できた。これもmap1を使用していたりしてひと捻りあったりする。

;; 動作
(with-gensyms (foo bar)
  foo)

;->(LET ((FOO (GENSYM)) (BAR (GENSYM)))
;  FOO)

お題

aif2

暗記で再現

(defmacro aif2 (test conseq &optional (else nil))
  (with-gensyms (v2)
    `(multiple-value-bind (it ,v2) ,test
       (if ,v2
	   ,conseq
	   (macrolet ((setf-it (val) 
                        (list 'setf ',test val)))
	     ,else)))))

できた。オリジナルは、(list 'setf ',test val)だけれども、`(setf ,',test ,val)と書いたらまずいんだろうか。

else節のsetf-itの動作でちょっと謎があって、setf-itの後でもitは前の値のままになっているので、

(let ((foo "foo") bar)
  (aif2 (values foo bar)
	(print it)
	(progn
	  (setf-it (values "new" "new"))
          (print it)))
  foo)
;"foo"と印字され"new"を返す。

というような動作をします。

(defmacro aif2 (test conseq &optional (else nil))
  (with-gensyms (v2)
    `(multiple-value-bind (it ,v2) ,test
       (if ,v2
	   ,conseq
	   (macrolet ((setf-it (val) 
			`(setq it (setf ,',test ,val))))
	     ,else)))))

なら、else節のitに再度代入するので、setf-itの後は更新された値にはなりますが、やっぱりitが更新されないってのは何らかの意図があってのことなんでしょうか。