11.1 Creating Context
Context here has two senses. One sort of context is a lexical environment.
The let special form creates a new lexical environment; the expressions in the body of a let will be evaluated in an environment which may contain new variables.
If x is set to a at the toplevel, then
(let ((x 'b)) (list x))with nonetheless return (b), because the call to list will be made in an environment containing a new x, whose value is b.
; Fig 11.1 : Macro implementations of let.
; let 만들어보자.
; 1 binds는 바인딩 되는 리스트 body는 이제 표현식
; 2 mapcar로 binds에서 (car x)로 키 값만 가져온다.
; 2.1 cons가 아니면 x로
; 2.2 그리고 그것은 매개변수에 들어간다. (lambda 매개변수 ..)
; 2.3 body를 ,@body로 리스트를 벗긴다.
; 3 이제 람다 안에 binds 안에 값을 넣는다.
; 3.1 mapcar로 binds에서 (cdr x)로 밸류만 가져온다.
; 3.2 cons가 아니면 nil로 바인딩
(defmacro our-let (binds &body body)
`((lambda ,(mapcar #'(lambda (x)
(if (consp x) (car x) x))
binds)
,@body)
,@(mapcar #'(lambda (x)
(if (consp x) (cadr x) nil))
binds)))
보면 알겟지만, let은 매크로 안에서 lambda에서 만든다.(our-let ((x 1) (y 2)) (+ x y)) ;; 아래로 확장 ((lambda (x y) (+ x y)) 1 2)
그림 11.2는 lexical 환경을 만들어서 바인딩하는 3개의 새로운 매크로를 제공한다.
; Fig 11.2 : Macros which bind variables.
(defmacro when-bind ((var expr) &body body)
`(let ((,var ,expr))
(when ,var
,@body)))
(defmacro when-bind* (binds &body body)
(if (null binds)
`(progn ,@body)
`(let (,(car binds))
(if ,(caar binds)
(when-bind* ,(cdr binds) ,@body)))))
(defmacro with-gensyms (syms &body body)
`(let ,(mapcar #'(lambda (s)
`(,s (gensym)))
syms)
,@body))
when-bind는 섹션 7.5에서 리스트 파라미터 구조분해로 쓰임. 그러므로 이 매크로는 94페이지에서 이미 설명이 됨(when-bind (input (get-user-input))
(process input))
;; expand
(let ((input (get-user-input)))
(when input
(process input)))
더 나아가서 when-bind*는 symbol expression(바인딩 될 심볼과 함수)쌍의 리스트를 받는다. -- let의 첫번째 매개변수와 같다.If any expression returns nil, the whole when-bind* expression returns nil.
이중에 하나라도 nil을 뱉으면 when-bind* 모두 nil임. nil 아니면 body가 평가되게 된다. 그리고 각 symbol들은 let*으로 묶인다.
(when-bind* ((x (find-if #'consp '(a (1 2) b)))
(y (find-if #'oddp x)))
(+ y 10))
11
마지막으로 with-gensyms 매크로다. 매크로를 작성할 때 쓰이는 녀석.많은 매크로들이 gensyms를 생성하는 것으로 시작한다.
종종 이게 많을 때가 있다. page 115에 with-redraw는 5개나 gensym로 만든다
(defmacro with-redraw ((var objs &body body)
(let ((gob (gensym))
(x0 (gensym)) (y0 (gensym))
(x1 (gensym)) (y1 (gensym)))
...))
;; 이제 이렇게
(defmacro with-redraw ((var objs) &body body)
(with-gensyms (gob x0 y0 x1 y1)
...))
만약 변수들을 바인딩하려할 때, 조건절에 따라서 다르게 평가를 한다면?
let안에 조건절을 사용하자.
하지만 반대로는? 조건에 따라서 바인딩이 달라져야 한다면
(defmacro condlet (clauses &body body)
(let ((bodfn (gensym))
(vars (mapcar #'(lambda (v) (cons v (gensym)))
(remove-duplicates
(mapcar #'car
(mappend #'cdr caluses))))))
'(labels ((,bodfn ,(mapcar #'car vars)
,@body))
(cond ,@(mapcar #'(lambda (cl)
(condlet-clause vars cl bodfn))
clauses)))))
(defun condlet-clause (vars cl bodfn)
`(,(car cl) (let ,(mapcar #'cdr vars)
(let ,(condlet-binds vars cl)
(,bodfn ,@(mapcar #'cdr vars))))))
(defun condlet-binds (vars cl)
(mapcar #'(lambda (bindform)
(if (consp bindform)
(cons (cdr (assoc (car bindform) vars))
(cdr bindform))))
(cdr cl)))
그림 11.3이 그런경우를 보여주는 것이다.(condlet (((= 1 2) (x (princ 'a)) (y (princ 'b)))
((= 1 1) (y (princ 'c)) (x (princ 'd)))
(t (x (princ 'e)) (z (princ 'f))))
(list x y z))
CD
(D C NIL)
11.2 The with- Macro
with-형태의 매크로를 만들어서 컨텍스트를 생성한다.
(with-open-file (s "dump" :direction :output) (princ 99 s))이러면 저절로 "dump"파일을 닫히고 99가 써있게 될 것이다.
; pure macro
(defmacro with-db (db &body body)
(let ((temp (gensym))
`(let ((,temp *db*))
(unwind-protect
(progn
(setq *db* ,db)
(lock *db)
,@body)
(progn
(release *db*)
(setq *db* ,temp))))))
; with function
(defmacro with-db (db &body body)
(let ((gbod (gensym)))
`(let ((,gbod #'(lambda () ,@body)))
(declare (dynamic-extent ,gbod))
(with-db-fn *db* ,db ,good))))
(defun with-db-fn (old-db new-db body)
(unwind-protect
(progn
(setq *db* new-db)
(lock *db*)
(funcall body))
(progn
(release *db*)
(setq *db* old-db))))
다른 매크로도 보자.; Fig 11.5: Macros for conditional evaluation.
(defmacro if3 (test t-case nil-case ?-case)
`(case ,test
((nil) ,nil-case)
(? ,?-case)
(t ,t-case)))
(defmacro nif (expr pos zero neg)
(let ((g (gensym)))
`(let ((,g ,expr))
(cond ((plusp ,g) ,pos)
((zerop ,g) ,zero)
(t ,neg)))))
11.3 Conditional Evaluation 조건평가
;; fig 11.6 : Macros for conditional evaluation.
(defmacro in (obj &rest choices)
(let ((insym (gensym)))
`(let ((,insyn ,obj))
(or ,@(mapcar #'(lambda (c) `(eql ,insym ,c))
choices)))))
(defmacro inq (obj &rest args)
`(in ,obj ,@(mapcar #'(lambda (a) '' ,a)
args)))
(defmacro in-if (fn &rest choices)
(let ((fnsym (gensym)))
`(let ((,fnsym ,fn))
(or ,@(mapcar #'(lambda (c)
`(funcall ,fnsym ,c))
choices)))))
(defmacro >case (expr &rest clauses)
(let ((g (gensy)))
`(let ((,g ,expr))
(cond ,@(mapcar #'(lambda (cl) (>casex g cl))
clauses)))))
(defun >casex (g cl)
(let ((key (car cl)) (rest (cdr cl)))
(cond ((consp key) `((in ,g ,@key) ,@rest))
((inq key t otherwise) `(t ,@rest))
(t (error "bad >case clause")))))
11.4 Iteration 반복
; 11.7 : Simple iteration macros.
(defmacro while (test &body body)
`(do ()
((not ,test))
,@body))
(defmacro till (test &body body)
`(do ()
(,test)
,@body))
(defmacro for ((var start stop) &body body)
(let ((gstop (gensym)))
`(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))
댓글 없음:
댓글 쓰기