; 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)
댓글 없음:
댓글 쓰기