; 7. Macros
(defmacro nil! (var)
`(setq ,var nil))
(defmacro nif (expr pos zero neg)
`(case (truncate (signum ,expr))
(1 ,pos)
(0 ,zero)
(-1 ,neg)))
(defmacro while (test &body body)
`(do ()
((not ,test))
,@body))
(print "macroexpand '(while (able) (laugh))))")
(pprint (macroexpand '(while (able) (laugh))))
(print "macroexpand-1 '(while (able) (laugh))))")
(pprint (macroexpand-1 '(while (able) (laugh))))
(defmacro mac (expr)
`(pprint (macroexpand-1 ',expr)))
; 7.5 Destructuring in Parameter Lists
; destructuring-bind macro (new in CLTL2) takes a pattern,
; an argument evaluating to a list, and a body of expressions,
; and evaluates the expressions with the parameters in the pattern bound
; to the corresponding elements of the list
(print "(destructuring-bind (x (y) . z) '(a (b) c d))")
(print (destructuring-bind (x (y) . z) '(a (b) c d)
(list x y z)))
(defmacro our-dolist ((var list &optional result) &body body)
`(progn
(mapc #'(lambda (,var) ,@body)
,list)
(let ((,var nil))
,result)))
(defmacro when-bind ((var expr) &body body)
`(let ((,var ,expr))
(when ,var
,@body)))
; called as follows:
; (when-bind (input (get-user-input))
; (process input))
; instead of:
; (let ((input (get-user-input)))
; (when input
; (process input)))
;7.6 A model of Maros
; Since defmacro is itself a macro, we can give it the same treatment.
; This definition uses several techniques which haven't been covered yet,
; so some readers may want to refer to it later.
; Fig 7.6 A sketch of defmacro
(defmacro our-expander (name) `(get ,name 'expander))
(defmacro our-defmacro (name params &body body)
(let ((g (gensym)))
`(progn
(setf (our-expander ',name)
#'(lambda (,g)
(block ,name
(destructuring-bind ,params (cdr ,g)
,@body))))
',name)))
(defmacro our-defmacro (name params &body body)
(let ((g (gensym)))
`(progn
(setf (our-expander ',name)
#'(lambda (,g)
(block ,name
(destructuring-bind ,params (cdr ,g)
,@body))))
',name)))
(defun our-macroexpand-1 (expr)
(if (and (consp expr) (our-expander (car expr)))
(funcall (our-expander (car expr)) expr)
expr))
;; 매크로 기능들을 직접 만듬.
; 7.7 Macros as Programs
; A macro is a function which transforms one sort of expression into another.
; 지금까지 쉬운 매크로였다. 하지만 do 처럼 어려운 예라면?
; 그림7.7에 do가 어떻게 확장되는지 보여준다.
; Fig 7.7
(print "Fig 7.7 do")
(do ((w 3)
(x 1 (1+ x))
(y 2 (1+ y))
(z))
((> x 10) (princ z) y)
(princ x)
(princ y))
(print "Fig 7.7 do expand")
(prog ((w 3) (x 1) (y 2) (z nil))
foo ;goto statement
(if (> x 10)
(return (progn (princ z) y)))
(princ x)
(princ y)
(psetq x (1+ x) y (1+ y))
(go foo))
; 이제 이걸로 do를 만들어보자.
; Fig 7.8 Implementing do.
(defmacro our-do (bindforms (test &rest result) &body body)
(let ((label (gensym)))
`(prog ,(make-initforms bindforms)
,label
(if ,test
(return (progn ,@result)))
,@body
(psetq ,@(make-stepforms bindforms))
(go ,label))))
; 위 예제에서 ((w 3) (x 1) (y 2) (z nil)) 이거 만드는 곳
(defun make-initforms (bindforms)
(mapcar #'(lambda (b)
(if (consp b)
(list (car b) (cadr b))
(list b nil)))
bindforms))
; mapcan는 mapcar와 비슷하지만 nconc를 사용한다.(mapcar는 list)
; 그러므로 값이 nil이면 리턴값에는 들어가지 않는다.
(defun make-stepforms (bindforms)
(mapcan #'(lambda (b)
(if (and (consp b) (third b))
(list (car b) (third b))
nil))
bindforms))
; 이건 실제구현과는 다르다고 한다.
; 아래 2개로 쪼갠 make-initforms, make-stepforms는 나중에 defmacro 안으로 들어갈 것이다.
; Fig 7.8 Macro Style
; 좋은 스타일은 매크로에서는 다르게 의미할 수 있다.
; Style matters when code is either read by people or evaludated by Lisp.
; With macros, both of these activites take place under slightly unusual circumstances.
; 매크로 정의에서 두 가지 종류의 코드가 있다.
; 1. expander code, the code used by the macro to generate its expansion
; 2. expansion code, which appears in the expansion itself.
; expander code can favor clarity over efficiency, and expansion code can favor efficiency over clarity.
; expander코드는 확장을 생성하는 코드. expansion code는 확장된 코드 그 자체
; 예를보자. (and a b c)는 (if a (if b c))와 같다.
; and를 그림 7.9에서 만들어보자.
; our-and는 속도측면에서보는 보면 별로다. 이 확장코드는 재귀적이며, 각 재귀는 같은 리스트의 cdr 길이를 잰다.
; 만약 이 코드가 런타임에 평가된다면, our-andb가 좀 더 나을 것이다.
; 하지만 our-and가 더 좋지는 않더라도 좋은 정도라고 할 수 있다.
; length를 재귀적으로 계속 호출해서 비효율적일 수는 있으나, 코드가 좀 더 깨끗하게 느껴진다.
; Fig 7.8 Two macros equivalent to and.
(defmacro our-and (&rest args)
(case (length args)
(0 t)
(1 (car args))
(t `(if ,(car args)
(our-and ,@(car args))))))
(defmacro our-andb (&rest args)
(if (null args)
t
(labels ((expander (rest)
(if (cdr rest)
`(if ,(cdr rest)
,(expander (cdr rest)))
(car rest))))
(expander args))))
; expander code에서
; (a) only affects the speed of compilation
; (b) doesn't affect it very much -- meaning the clarity should nearly always come first
; expansion code, it's just the opposite
; Clarity matters less for macro expansions because they are rarely looked at.
; expansion코드에는 클린코드가 낫다. 확장되서 만들어진 코드는 더러워도 괜찮다.
;7.9 Dependence on Macros
;1. Define macros before functions (or macros) which call them.
;2. When a macro is redefined, also recompile all the functions (or macros)
; which call it -- directly or via other macros.
; 7.10 Macros from Functions
; 함수에서 매크로로 변경하는 방법을 배울 것. 문제는 정말 필요한가이다.
; 몇가지 정당화할 이유가 있다.
; When you begin writing macros, it sometimes helps to think as if you were writing a function
; 매크로를 만들려고 할 때, 함수를 만드는 것처럼 생각하면 도움이 크게 된다.
; 다른 하나는 매크로/함수 살펴보면, 서로의 차이를 제대로 볼 수 있다는 점.
; 마지막으로 리스프 개발자는 가끔 실제로 함수를 매크로로 바꾸고 싶어한다.
;(defun second (x) (cadr x))
(defmacro second2 (x) `(cadr ,x))
; (defun noisy-second (x)
; (princ "Someone is taking a cadr!")
; (cadr x))
(defmacro noisy-second (x)
`(progn
(princ "Someone is taking a cadr!")
(cadr ,x)))
; (defun sum (&rest args)
; (apply #'+ args))
(defmacro sum (&rest args)
`(apply #'+ (list ,@args)))
(defmacro sum2 (&rest args)
`(+ ,@args))
;(defun foo (x y z)
; (list x (let ((x y))
; (list x z))))
(defmacro foo (x y z)
`(list ,x (let ((x ,y))
(list x ,z))))
; 7.11 Symbol Macros
; CLTL2는 symbol-macro라는 새로운 매크로를 제시함.
; 일반 매크로는 함수 호출처럼 호출한다.
; symbol-macro의 "호출"은 심볼같다(?)
; Symbol-macro는 오로지 로컬에서만 정의된다.
(symbol-macrolet ((hi (progn (print "Howdy")
1)))
(+ hi 2))
; "Howdy"
; 3
; 나중에 더 알아볼 것임. 15쯤
2019년 8월 29일 목요일
[on lisp] 7 매크로 (여러가지 매크로 정의들)
피드 구독하기:
댓글 (Atom)
댓글 없음:
댓글 쓰기