2019년 9월 11일 수요일

[on lisp] 12.2 The Multiple Evaluation Problem

;gnu clisp 2.49

(print "Hello, world!")

(defmacro toggle (obj)
    `(setf ,obj (not ,obj)))


(print
;; 빡치게 두번 실행됨. 한번만 실행되어야 하는데
;; 토글 안에서 obj를 두개 넣기 때문임.
(let ((lst '(t nil t))
      (i -1))
     (toggle (nth (incf i) lst))
     lst)
 )
(T NIL T) ; 안됨 첫번째 T가 NIL이 되어야함.
;; 형태를 더 보면
(setf (nth (incf i) lst)
      (not (nth (incf i) lst)))
;; 이렇게 (incf i)를 두번 부르게 된다.

;; 'toggle'의 표현식 인수를 단순히 'setf'의 첫 번째 인수로 삽입하는 것만으로는 충분하지 않다.

; 우리는 들어온 표현식의 내부를 확인해봐야 한다.
; 하위 양식이 포함 된 경우, 부작용이있는 경우 하위 양식을 분리하여 개별적으로 평가해야합니다.

; 쉽게 만들기 위해. Common Lisp는 setf에서 제한된 매크로 클래스를 자동으로 정의하는 매크로를 제공.
; 이 매크로가 setf의 제한된 매크로 클래스 인 'defined-modify-macro'.
; 이름, 추가적인 매개변수(after the generalized variable), 그리고 함수이름 3개의 매개변수를 받는다.

; Using define-modify-macro, we could define toggle as follows:
; (define-modify-macro toggle () not)
; 이 코드는 "to evaluate an expression of the form (toggle place), find the location specified by place,
; and if the value stored there is val, replace it with the value of (not val)"이다.
; (toggle place)형태의 표현식을 평가하려면, place로 특정되는 위치를 찾고, 값이 있다면 그걸 val로 하자, 그 val을 (not value)로 치환하라.
(define-modify-macro toggle () not)
;; 이렇게만 하면 된다. 테스트해보자.
(let ((lst '(t nil t))
      (i -1))
     (toggle (nth (incf i) lst))
     lst)
(NIL NIL T) ; 잘됨

;; 좀더 일반화 할 수 있다.
;setf와 setq는 임의의 갯수의 인수를 취할 수 있으므로 toggle도 마찬가지로 가능할 것이다.
; modify-macro 위에 다른 매크로를 정의하여이 기능을 추가 할 수 있습니다.
;Fig 12.1로 더 자세히 보자.
(defmacro allf (val &rest args)
    (with-gensyms (gval)
        `(let ((,gval ,val))
              (set ,@(mapcan #'(lambda (a) (list a gval))
                             args)))))

(defmacro nilf (&rest args) `(allf nil ,@args))

(defmacro tf (&rest args) `(allf t ,@args))

(defmacro toggle (&rest args)
    `(progn
         ,@(mapcar #'(lambda (a) '(toggle2 ,a))
                   args)))
(define-modify-macro toggle2 () not)
;; 이건 이전에 만든 토글
(defmacro toggle (obj)
    `(setf ,obj (not ,obj)))

댓글 없음:

댓글 쓰기