2012-08-07 4 views
2

push을 매크로로 구현하는 방법을 이해할 수 있습니까? 순진 버전은 아래 두 장소 양식을 평가하고, 요소 양식을 평가하기 전에 그렇게 :푸시 매크로를 구현하려면 어떻게해야합니까?

(defmacro my-push (element place) 
    `(setf ,place (cons ,element ,place))) 

을하지만 다음 다음과 같이이 문제를 해결하려고하면 나는 setf가 잘못된 장소 -ing 해요 :

(defmacro my-push (element place) 
    (let ((el-sym (gensym)) 
     (place-sym (gensym))) 
    `(let ((,el-sym ,element) 
      (,place-sym ,place)) 
     (setf ,place-sym (cons ,el-sym ,place-sym))))) 

CL-USER> (defparameter *list* '(0 1 2 3)) 
*LIST* 
CL-USER> (my-push 'hi *list*) 
(HI 0 1 2 3) 
CL-USER> *list* 
(0 1 2 3) 

setf을 두 번 평가하지 않고 어떻게 올바른 위치에 저장할 수 있습니까?

+0

SBCL 소스를 살펴보면 rplaca를 처리하기 위해 한 번만 사용하는 매크로를 사용합니다. 자세한 내용은이 링크를 참조하십시오. http://stackoverflow.com/questions/9808928/understanding-how-to-implement-once -only-lisp-macro). 나는 더 많은 이해를 위해 rplaca를 조사 할 것이다. –

+0

@PaulNathan보고있는 SBCL 버전과 소스 위치를 제공해 주시겠습니까? 'once-only'를 사용하지 않는'src/code/early-setf.lisp' (SBCL 1.0.58)에서'(defmacro-mundanely push (obj place & environment env) ... '를 보았 기 때문에) –

답변

3

는이 권리를 이렇게하면 좀 더 복잡한 것 같다. 예를 들어, SBCL 1.0.58에서 push에 대한 코드는 다음과 같습니다

(defmacro-mundanely push (obj place &environment env) 
    #!+sb-doc 
    "Takes an object and a location holding a list. Conses the object onto 
    the list, returning the modified list. OBJ is evaluated before PLACE." 
    (multiple-value-bind (dummies vals newval setter getter) 
     (sb!xc:get-setf-expansion place env) 
    (let ((g (gensym))) 
     `(let* ((,g ,obj) 
       ,@(mapcar #'list dummies vals) 
       (,(car newval) (cons ,g ,getter)) 
       ,@(cdr newval)) 
     ,setter)))) 

그래서 get-setf-expansion에 문서를 읽는 것이 유용 할 것으로 보인다.기록을 위해

가, 생성 된 코드는 아주 좋은 같습니다

는 심볼로 밀어하십시오 setf의-수있는 기능으로 밀어

(LET* ((#:G906 1) (#:NEW905 (CONS #:G906 SYMBOL))) 
    (SETQ SYMBOL #:NEW905)) 

(push 1 symbol) 

가 확장 (symbol 가정 목록을 가리킴) :

(push 1 (first symbol)) 

는 (여전히 심지어 공부 한 후 그렇게 보일 수 있습니다) setf, setf expansions 및 회사가이 오히려 비밀 보이는 공부 시간이 걸릴 그래서하지 않는

(LET* ((#:G909 1) 
     (#:SYMBOL908 SYMBOL) 
     (#:NEW907 (CONS #:G909 (FIRST #:SYMBOL908)))) 
    (SB-KERNEL:%RPLACA #:SYMBOL908 #:NEW907)) 

로 확장합니다. OnLisp의 '일반화 된 변수'장도 유용 할 수 있습니다.

힌트 : 자신의 SBCL을 컴파일하면 (그다지 열심히) --fancy 인수를 make.sh으로 전달하십시오. 이 방법을 사용하면 SBCL에서 함수/매크로의 정의를 빠르게 볼 수 있습니다 (예 : M-. Emacs + SLIME 내부). 분명히 그 소스를 삭제하지 마십시오 (을 install.sh 이후에 실행하면 공간의 90 %를 절약 할 수 있습니다).

1

기존 (SBCL에서은, 적어도) 일을하는 방법을 살펴보면, 나는 참조 :

* (macroexpand-1 '(push 1 *foo*)) 

(LET* ((#:G823 1) (#:NEW822 (CONS #:G823 *FOO*))) 
    (SETQ *FOO* #:NEW822)) 
T 

그래서, 나는 버전의 조합 이것이 하나를 생성에 혼합, 상상 할 수 있습니다

(defmacro my-push (element place) 
    (let ((el-sym (gensym)) 
     (new-sym (gensym "NEW"))) 
    `(let* ((,el-sym ,element) 
      (,new-sym (cons ,el-sym ,place))) 
     (setq ,place ,new-sym))))) 

몇 가지 관찰 :

  1. 이 함께 일하는 것 중 하나을또는 setf 실제로 해결하려고하는 문제 (push을 다시 쓰는 것이 최종 목표는 아닙니다)에 따라 어느 쪽을 선호 할지도 모릅니다.

  2. 여전히 place은 여전히 ​​두 번 평가됩니다 ... 적어도 element을 평가 한 후에 만 ​​평가됩니다. 이중 평가가 실제로 피할 필요가있는 것이 있습니까? (내장 된 push이 없다는 것을 감안할 때, 나는 당신이 할 수 있을지 궁금해한다. 그러나 그것에 대해 생각할 때가 많은 시간을 보내기 전에 이것을 쓰고있다.) 'place'으로 평가해야합니다. 아마도 정상입니까?

  3. let 대신 let*을 사용하면 ,new-sym의 설정에서 ,el-sym을 사용할 수 있습니다. 이것은 cons이 발생하는 곳으로 이동하여 첫 번째 평가에서 ,place의 평가를 받고 ,element의 평가 후에 평가됩니다. 아마도 이것은 평가 주문과 관련하여 필요한 것을 얻을 수 있습니까?

  4. 두 번째 버전의 가장 큰 문제점은 setf이 실제로 전달 된 심볼에서 작동해야하며 gensym 심볼이 아닌 것입니다.

는 희망이 (난 아직도 자신을 모든이 다소 새로운 해요, 그래서 여기에 몇 가지 추측을 만들고있어.) ... 도움이

+1

흥미 롭습니다 .HeperSpec은 장소 양식은 한 번만 평가되지만 SBCL은 기호에서 값을 두 번 가져 오는 것이 무해하다는 것을 알 수 있습니다. 가능한 부작용이있는 장소 양식의 경우 : CL-USER> (macroexpand-1 (내 목록 FUNC))) # : G30 (FUNCALL (# : G31 4) # : G30 (CONS # : G31 (내리스트 -FUNC)))) # 이 SETF 형식은 어떻게 든 두 번째 평가를 피할 수 있습니까? – jjoelson

+1

위의 코드를 실제로 실행할 때 "정의되지 않은 함수 (SETF MY-LIST-FUNC)"가 표시됩니다. 스스로 평가할 수없는 장소 양식을 허용하지 않음으로써 이중 평가 문제와 같은 문제를 피할 수 있습니다. – jjoelson

관련 문제