fig 11.10을 보자
(defmacro mvdo* (parm-cl test-cl &body body)
(mvdo-gen parm-cl parm-cl test-cl body))
(defun mvdo-gen (binds rebinds test body)
(if (null binds)
(let ((label (gensym)))
`(prog nil
,label
(if ,(car test)
(return (progn ,@(cdr test))))
,@body
,@(mvdo-rebind-gen rebinds)
(go ,label)))
(let ((rec (mvdo-gen (cdr binds) rebinds test body)))
(let ((var/s (caar binds)) (expr (cadar binds)))
(if (atom var/s)
`(let ((,var/s ,expr)) ,rec)
`(multiple-value-bind ,var/s ,expr ,rec))))))
(defun mvdo-rebind-gen (rebinds)
(cond ((null rebinds) nil)
((< (length (car rebinds)) 3)
(mvdo-rebind-gen (cdr rebinds)))
(t
(cons (list (if (atom (caar rebinds))
'setq
'multiple-value-setq)
(caar rebinds)
(third (car rebinds)))
(mvdo-rebind-gen (cdr rebinds))))))
mvdo*를 써보자
(mvdo* ((x 1 (1+ x))
((y z) (values 0 0) (values z x)))
((> x 5) (list x y z))
(princ (list x y z)))
(1 0 0)(2 0 2)(3 2 3)(4 3 4)(5 4 5)
(6 4 5)
와... 이렇게 여러개 반복이 멋지게 된단 말이다. 이런 식의 반복은 꽤나 유용하다.
그래픽 프로그램에서는 종종 좌표나 지역 같은 다중 값들을 한번에 다뤄야 한다.
단순한 대화형(반응형) 게임을 작성한다 할때, 목표는 두 물체 사이에 찌그러지지 않도록 하는 것이다.
만약 두 물체가 동시에 당신을 맞추면 지는 것.
혹은 두 물체가 서로를 부신다면 이기는 것.
그림 11.11 에서 이런 게임을 만들 때 mvdo*가 얼마나 유용할지 보여줄 것.
(mvdo* (((px py) (pos player) (move player mx my))
((x1 y1) (pos obj1) (move obj1 (- px x1)
(- py y1)))
((x2 y2) (pos obj2) (move obj2 (- px x2)
(- py y2)))
((mx my) (mouse-vector) (mouse-vector))
(win nil (touch obj1 obj2))
(lose nil (and (touch obj1 player)
(touch obj2 player))))
((or win lose) (if win 'win 'lose))
(clear)
(draw obj1)
(draw obj2)
(draw player))
; (pos obj) return two values x, y representing the position of obj. ?Initially, the three objects have random positions
; (move obj dx dy) moves the obj depending on its type and the vector(dx,dy). returns two values x,y indicating the new position.
; (mouse-vector) returns two values dx, dy indicating the current movement of the mouse.
; (touch obj1 obj2) returns true if obj1 and obj2 are touching
; (clear) clear the game region
; (draw obj) draws obj at its current position
아래는 코드를 분석해본 것
(defmacro mvdo* (parm-cl test-cl &body body)
(mvdo-gen parm-cl parm-cl test-cl body))
(defun mvdo-gen (binds rebinds test body)
(if (null binds) ; binds가 없다면(재귀 다돌면)
(let ((label (gensym)))
`(prog nil
,label
(if ,(car test)
(return (progn ,@(cdr test))))
,@body
,@(mvdo-rebind-gen rebinds)
(go ,label)))
(let ((rec (mvdo-gen (cdr binds) rebinds test body))) ;; binds가 있다면. (mvdo-gen (cdr binds) rebinds test body)를 rec로 부른다. (재귀할건가봄)
(let ((var/s (caar binds)) (expr (cadar binds))) ;; binds의 첫번째의 심볼값을 var/s라 하고 그 옆에 있는 녀석을 expr로 넣는다.(초기값)
(if (atom var/s) ; var/s가 하나라면 (다중이 아니라면)
`(let ((,var/s ,expr)) ,rec) ; (,var/s ,expr) 로 값을 바인딩함 그 후 rec를 실행 (mvdo*는 순서대로 실행되어야함 바인딩이 순서가 있음)
`(multiple-value-bind ,var/s ,expr ,rec)))))) ; 여러개면 multiple-value-bind를 쓴다. (multiple-value-bind (...) (...) rec) rec에서 let과 muliple-value-bind가 계속 만들어짐
; 결국 재귀를 하다가 (if (null binds)...) 에 다다르면
; (prog를 쓰고 (return (progn ,@(cdr test)))로 리턴할 값을 리턴함.
; cadar는 cdr후 car한것
; 데모
'(mvdo* ((x 1 (1+ x))
((y z) (values 0 0) (values z x)))
((> x 5) (list x y z))
(princ (list x y z)))
))
;(LET ((X 1))
; (MULTIPLE-VALUE-BIND (Y Z) (VALUES 0 0)
; (PROG NIL #:G3210 (IF (> X 5) (RETURN (PROGN (LIST X Y Z)))) ; PROG NIL(바인딩없음) #:G3210 고투문을 위한 이름
; (PRINC (LIST X Y Z)) ; ,@body
; (SETQ X (1+ X)) ; ,@(mvdo-rebind-gen rebinds)
; (MULTIPLE-VALUE-SETQ (Y Z) (VALUES Z X)) (GO #:G3210)))) ; (go ,label)))
; rebinds가 nil이면 nil
; (< (length (car rebinds)) 3) 길이가 3보다 작으면(2개면 값설정만 3개면 루프돌때마다 업데이트) (mvdo-rebind-ged (cdr rebinds))
;
(defun mvdo-rebind-gen (rebinds)
(cond ((null rebinds) nil)
((< (length (car rebinds)) 3)
(mvdo-rebind-gen (cdr rebinds)))
(t
(cons (list (if (atom (caar rebinds)) ; 바인딩될 값이 atom이면 setq
'setq
'multiple-value-setq) ; 여러개면 multiple-value-setq
(caar rebinds) ; caar로 첫번째(현재바인딩문)의 첫번째 빼냄.(심볼)
(third (car rebinds))) ; 업데이트문 빼냄.
(mvdo-rebind-gen (cdr rebinds)))))) ; cdr로 다음타자 재귀로 부름. 마지막에 cons로 합침.
; 데모
(print (macroexpand
(mvdo-rebind-gen '((x 1 (1+ x))
((y z) (values 0 0) (values z x))))))
; ((SETQ X (1+ X)) (MULTIPLE-VALUE-SETQ (Y Z) (VALUES Z X)))
'(print (caar '((y z) (values 0 0) (values z x))))
댓글 없음:
댓글 쓰기