2

push誰かがマクロとして実装する方法を理解するのを手伝ってもらえますか?以下の単純なバージョンは、場所のフォームを2回評価し、要素のフォームを評価する前に評価します。

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

しかし、これを以下のように修正しようとするとsetf、間違った場所に移動します:

(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)

setf2回評価せずに正しい場所を設定するにはどうすればよいですか?

4

2 に答える 2

4

この権利を行うことはもう少し複雑なようです。たとえば、pushSBCL1.0.58のコードは次のとおりです。

(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に関するドキュメントを読むことは有用であるように思われます。

記録として、生成されたコードは非常に見栄えがします。

シンボルに押し込む:

(push 1 symbol)

に拡大

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

SETF対応関数へのプッシュ(symbolリストのリストへのポイントを想定):

(push 1 (first symbol))

に拡大

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

したがって、勉強setfsetf拡張、会社に時間をかけない限り、これはかなり難解に見えます(それらを勉強した後でもそう見えるかもしれません)。OnLispの「一般化された変数」の章も役立つ場合があります。

ヒント:独自のSBCLをコンパイルする場合(それほど難しくはありません)、--fancy引数をに渡しますmake.sh。このようにして、SBCL内の関数/マクロの定義をすばやく確認できます(たとえば、M-.Emacs + SLIME内)。もちろん、これらのソースは削除しないでください(スペースの90%を節約するために、のclean.sh後に実行できます)。install.sh

于 2012-08-10T06:25:55.640 に答える
1

既存のもの(少なくともSBCLでは)がどのように動作するかを見てみると、次のようになります。

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

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

だから、私はあなたのバージョンとこれが生成するものの組み合わせを混ぜ合わせることを想像します、1つはするかもしれません:

(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. これは、またはのいずれかで機能するようsetqですsetf。実際に解決しようとしている問題に応じて(書き直しpushは実際の最終目標ではないと思います)、どちらか一方を優先することができます。

  2. それでも2回評価されることに注意してくださいplace...少なくとも評価後にのみ評価されますがelement。二重評価は実際に避ける必要があるものですか?(ビルトインがそうでpushはないことを考えると、私はあなたがそれを考えるのにひどく多くの時間を費やす前にこれを書いていますが、あなたができるかどうか/どうやってできるのか疑問に思っています。) 「場所」として評価する必要があります、おそらくこれは正常ですか?

  3. let*の代わりに使用すると、の設定でlet使用できます。これは、の最初の評価で評価され、の評価の後に評価されるように、発生した場所に移動します。おそらくこれは、評価の順序に関して、あなたが必要とするものを手に入れますか?,el-sym,new-symcons,place,element

  4. setf2番目のバージョンの最大の問題は、シンボルではなく、渡されたシンボルを実際に操作する必要があることだと思いますgensym

うまくいけば、これが役立つことを願っています...(私はまだこれすべてに少し慣れていないので、ここでいくつか推測しています。)

于 2012-08-08T20:17:12.597 に答える