문제

나는 의미상의 이유로 다른 프로그래밍 언어의 ++와 동등한 기능을 수행하는 Lisp 매크로를 작성하려고 시도했습니다.나는 여러 가지 다른 방법으로 이 작업을 시도했지만 그 중 어느 것도 작동하지 않는 것 같고 모든 것이 통역사에 의해 허용되므로 올바른 구문이 있는지 여부를 알 수 없습니다.이것이 어떻게 정의될지에 대한 나의 생각은 다음과 같습니다.

(defmacro ++ (variable)
  (incf variable))

하지만 이를 사용하려고 하면 SIMPLE-TYPE-ERROR가 발생합니다.무엇이 작동하게 만들까요?

도움이 되었습니까?

해결책

매크로는 평가할 표현식을 반환한다는 점을 기억하세요.이렇게 하려면 다음을 역따옴표로 묶어야 합니다.

(defmacro ++ (variable)
   `(incf ,variable))

다른 팁

이전 답변은 모두 작동하지만 다음과 같이 호출하는 매크로를 제공합니다.

(++ varname)

varname++ 또는 ++varname 대신에 당신이 원하는 것으로 생각됩니다.전자를 실제로 얻을 수 있는지는 모르겠지만 후자의 경우 읽기 매크로를 수행할 수 있습니다.두 문자이므로 디스패치 매크로가 가장 좋습니다.편리한 실행 lisp가 없기 때문에 테스트되지 않았지만 다음과 같습니다.

(defun plusplus-reader (stream subchar arg)
   (declare (ignore subchar arg))
   (list 'incf (read stream t nil t)))
(set-dispatch-macro-character #\+ #\+ #'plusplus-reader)

실제로 ++var를 만들어야 합니다 읽다 (incf var)로.

incf에 별칭을 만들지 말 것을 강력히 권합니다."이게 뭐지?"라고 자문해야 하는 코드를 읽는 다른 사람의 가독성이 떨어집니다.incf와 어떻게 다릅니까?"

간단한 사후 증가를 원하면 다음을 시도하십시오.

(defmacro post-inc (number &optional (delta 1))
  "Returns the current value of number, and afterwards increases it by delta (default 1)."
  (let ((value (gensym)))
    `(let ((,value ,number))
       (incf ,number ,delta)
       ,value)))

구문 (++ a) 에 대한 쓸모없는 별칭입니다 (incf a).그러나 사후 증분의 의미를 원한다고 가정해 보겠습니다.이전 값을 검색합니다.Common Lisp에서는 다음과 같이 수행됩니다. prog1, 다음과 같이: (prog1 i (incf i)).Common Lisp는 신뢰할 수 없거나 모호한 평가 순서로 인해 어려움을 겪지 않습니다.앞의 표현은 다음을 의미합니다. i 평가되고 그 값은 어딘가에 숨겨집니다. (incf i) 평가된 후 숨겨진 값이 반환됩니다.

완전 방탄 만들기 pincf (우편-incf) 완전히 사소한 것은 아닙니다. (incf i) 좋은 속성이 있어요 i 한 번만 평가됩니다.우리는 원한다 (pincf i) 그 속성도 갖게 되니까요.따라서 간단한 매크로는 부족합니다.

(defmacro pincf (place &optional (increment 1))
  `(prog1 ,place (incf ,place ,increment))

이를 올바르게 수행하려면 Lisp의 "할당 장소 분석기"라는 도구를 사용해야 합니다. get-setf-expansion 매크로가 액세스를 적절하게 컴파일할 수 있도록 하는 자료를 얻으려면:

(defmacro pincf (place-expression &optional (increment 1) &environment env)
  (multiple-value-bind (temp-syms val-forms
                        store-vars store-form access-form)
                        (get-setf-expansion place-expression env)
    (when (cdr store-vars)
      (error "pincf: sorry, cannot increment multiple-value place. extend me!"))
    `(multiple-value-bind (,@temp-syms) (values ,@val-forms)
       (let ((,(car store-vars) ,access-form))
         (prog1 ,(car store-vars)
                (incf ,(car store-vars) ,increment)
                ,store-form)))))

CLISP를 사용한 몇 가지 테스트.(메모:재료를 기반으로 한 확장 get-setf-expansion 구현별 코드가 포함될 수 있습니다.그렇다고 우리 매크로가 이식 가능하지 않다는 의미는 아닙니다!)

8]> (macroexpand `(pincf simple))
(LET* ((#:VALUES-12672 (MULTIPLE-VALUE-LIST (VALUES))))
 (LET ((#:NEW-12671 SIMPLE))
  (PROG1 #:NEW-12671 (INCF #:NEW-12671 1) (SETQ SIMPLE #:NEW-12671)))) ;
T
[9]> (macroexpand `(pincf (fifth list)))
(LET*
 ((#:VALUES-12675 (MULTIPLE-VALUE-LIST (VALUES LIST)))
  (#:G12673 (POP #:VALUES-12675)))
 (LET ((#:G12674 (FIFTH #:G12673)))
  (PROG1 #:G12674 (INCF #:G12674 1)
   (SYSTEM::%RPLACA (CDDDDR #:G12673) #:G12674)))) ;
T
[10]> (macroexpand `(pincf (aref a 42)))
(LET*
 ((#:VALUES-12679 (MULTIPLE-VALUE-LIST (VALUES A 42)))
  (#:G12676 (POP #:VALUES-12679)) (#:G12677 (POP #:VALUES-12679)))
 (LET ((#:G12678 (AREF #:G12676 #:G12677)))
  (PROG1 #:G12678 (INCF #:G12678 1)
   (SYSTEM::STORE #:G12676 #:G12677 #:G12678)))) ;
T

이제 주요 테스트 사례가 있습니다.여기 해당 장소에는 부작용이 있습니다. (aref a (incf i)).이는 정확히 한 번 평가되어야 합니다!

[11]> (macroexpand `(pincf (aref a (incf i))))
(LET*
 ((#:VALUES-12683 (MULTIPLE-VALUE-LIST (VALUES A (INCF I))))
  (#:G12680 (POP #:VALUES-12683)) (#:G12681 (POP #:VALUES-12683)))
 (LET ((#:G12682 (AREF #:G12680 #:G12681)))
  (PROG1 #:G12682 (INCF #:G12682 1)
   (SYSTEM::STORE #:G12680 #:G12681 #:G12682)))) ;
T

그래서 가장 먼저 일어나는 일은 A 그리고 (INCF I) 평가되어 임시 변수가 됩니다. #:G12680 그리고 #:G12681.배열에 액세스하고 값을 캡처합니다. #:G12682.그러면 우리는 우리의 PROG1 반환을 위해 해당 값을 유지합니다.값이 증가하고 CLISP를 통해 어레이 위치에 다시 저장됩니다. system::store 기능.이 저장 호출은 원래 표현식이 아닌 임시 변수를 사용합니다. A 그리고 I. (INCF I) 한 번만 나타납니다.

의미상 C++ 같은 언어에서 접두사 연산자 ++ 및 --는 일반 lisp의 incf/decf와 동일합니다.이것을 깨닫고 (잘못된) 매크로처럼 실제로 구문 변경을 찾고 있다면 `(incf ,x)와 같은 역따옴표를 사용하여 이를 수행하는 방법을 이미 본 것입니다.여러분은 독자가 이 문제를 해결하여 비리스프 구문에 더 가까운 것을 얻는 방법도 보여 주었습니다.하지만 문제는 이 두 가지 중 어느 것도 좋은 생각이 아니기 때문입니다.일반적으로 언어를 다른 언어와 더 유사하게 만들기 위해 관용적이지 않은 코딩은 그다지 좋은 생각이 아닙니다.

그러나 실제로 의미론을 찾고 있다면 이미 언급한 대로 접두사 버전을 가지고 있지만 후위 버전은 구문적으로 일치시키기 쉽지 않을 것입니다.충분한 독자 해커가 있으면 할 수 있지만 예쁘지는 않을 것입니다.

그것이 당신이 찾고 있는 것이라면 a) 관용적이고 잘 작동하므로 incf/decf 이름을 고수하고 b) post-incf, post-decf 버전을 작성하는 것이 좋습니다(예: defmacro post-incf (x) `(prog1 ,x (incf ,x)) 종류의 것입니다.

개인적으로 이것이 어떻게 특히 유용할지는 모르겠지만 ymmv입니다.

사전 증분에는 이미 incf가 있지만 다음을 사용하여 직접 정의할 수 있습니다.

(define-modify-macro my-incf () 1+)

사후 증분의 경우 다음을 사용할 수 있습니다(fare-utils에서).

(defmacro define-values-post-modify-macro (name val-vars lambda-list function)
 "Multiple-values variant on define-modify macro, to yield pre-modification values"
 (let ((env (gensym "ENV")))
   `(defmacro ,name (,@val-vars ,@lambda-list &environment ,env)
      (multiple-value-bind (vars vals store-vars writer-form reader-form)
          (get-setf-expansion `(values ,,@val-vars) ,env)
       (let ((val-temps (mapcar #'(lambda (temp) (gensym (symbol-name temp)))
                                 ',val-vars)))
          `(let* (,@(mapcar #'list vars vals)
                  ,@store-vars)
             (multiple-value-bind ,val-temps ,reader-form
               (multiple-value-setq ,store-vars
                 (,',function ,@val-temps ,,@lambda-list))
               ,writer-form
               (values ,@val-temps))))))))

(defmacro define-post-modify-macro (name lambda-list function)
 "Variant on define-modify-macro, to yield pre-modification values"
 `(define-values-post-modify-macro ,name (,(gensym)) ,lambda-list ,function))

(define-post-modify-macro post-incf () 1+)

물론 나는 그 발언과 주의 사항을 명심할 것입니다. 사이먼 그 사람 글에 달린 댓글을 보면 정말 그런 것 같아요. 사용자10029의 접근 방식은 여전히 ​​시도해 볼 가치가 있으므로 재미삼아 허용된 답변과 결합하여 ++x 연산자 작업(즉, 1에서 x 값을 증가시킵니다).시도 해봐!

설명:오래된 SBCL은 디스패치 문자 조회 테이블에 '+' 기호를 명시적으로 설정해야 하기 때문에 자신의 버전을 컴파일하지 않았습니다. make-dispatch-macro-character, 매크로는 변수를 평가하기 전에 변수 이름을 전달해야 합니다.따라서 이 작업을 수행해야 합니다.

(defmacro increment (variable)
  "The accepted answer"
  `(incf ,variable))

(make-dispatch-macro-character #\+) ; make the dispatcher grab '+'

(defun |inc-reader| (stream subchar arg)
  "sets ++<NUM> as an alias for (incf <NUM>).
   Example: (setf x 1233.56) =>1233.56
            ++x => 1234.56
            x => 1234.56"
   (declare (ignore subchar arg))
   (list 'increment (read stream t nil t)))

(set-dispatch-macro-character #\+ #\+ #'|inc-reader|)

보다 |inc-reader|'에스 독스트링 사용 예를 들어보세요.(밀접한) 관련 문서는 여기에서 찾을 수 있습니다.

이 구현으로 인해 +123과 같은 숫자 항목은 더 이상 이해되지 않습니다(디버거는 no dispatch function defined for #\Newline) 그러나 추가 해결 방법(또는 회피)이 합리적으로 보입니다.여전히 이것을 고수하고 싶다면 아마도 최선의 선택은 ++를 접두사로 사용하지 않고 ## 또는 기타 DSL 같은 솔루션을 사용하는 것입니다.

건배!

안드레스

이것은 트릭을 수행해야 하지만 나는 lisp 전문가가 아닙니다.

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top