6

セマンティック上の理由から、他のプログラミング言語で ++ と同等の機能を実行する Lisp マクロを作成しようと試みてきました。これをいくつかの異なる方法で実行しようとしましたが、どれも機能していないようで、すべてがインタープリターによって受け入れられるため、正しい構文があるかどうかわかりません。これがどのように定義されるかについての私の考えは

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

しかし、これを使用しようとすると SIMPLE-TYPE-ERROR が発生します。何がそれを機能させるでしょうか?

4

8 に答える 8

17

マクロは、評価される式を返すことに注意してください。これを行うには、バッククォートする必要があります。

(defmacro ++ (variable)
   `(incf ,variable))
于 2008-09-15T18:52:25.160 に答える
13

前の回答はどちらも機能しますが、次のように呼び出すマクロが提供されます

(++ varname)

varname++ または ++varname の代わりに、私はあなたが望んでいると思います。実際に前者が取れるかどうかはわかりませんが、後者は読み込みマクロができます。2 文字なので、おそらくディスパッチ マクロが最適です。私は便利な実行中の 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) として読み取る必要があります。

于 2008-09-15T20:54:35.800 に答える
11

構文(++ a)は の役に立たないエイリアスです(incf a)。しかし、ポストインクリメントのセマンティクスが必要だとします: 古い値を取得します。Common Lisp では、これは のように で行われprog1ます(prog1 i (incf i))。Common Lisp は、信頼できない、またはあいまいな評価順序に悩まされることはありません。前の式は、iが評価され、値がどこかにスタッシュ(incf i)され、評価され、スタッシュされた値が返されることを意味します。

完全な防弾pincf(post- incf) を作成することは、まったく簡単なことではありません。一度だけ評価される(incf i)ナイスプロパティを持っています。私たちもその財産を持ちiたいと思っています。(pincf i)したがって、単純なマクロでは不十分です。

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

get-setf-expansionこれを正しく行うには、マクロがアクセスを適切にコンパイルできるようにするための材料を取得するために呼び出される Lisp の「割り当て場所アナライザー」に頼る必要があります。

(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)). これは 1 回だけ評価する必要があります。

[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)が評価され、一時変数#:G12680and になり#:G12681ます。配列がアクセスされ、値が に取り込まれ#:G12682ます。次に、PROG1その値を保持して返すものがあります。system::store値がインクリメントされ、CLISP の関数を介して配列の場所に格納されます。この store 呼び出しは、元の式Aandではなく、一時変数を使用することに注意してくださいI(INCF I)一度だけ登場。

于 2012-05-12T22:13:42.073 に答える
10

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)))
于 2008-10-27T23:36:15.940 に答える
7

意味的には、c++ などの言語の前置演算子 ++ および -- は、common lisp の incf/decf と同等です。これに気付き、(間違った) マクロのように、実際に構文の変更を探している場合は、`(incf ,x) のようなバッククォートを使用してそれを行う方法を既に示しています。非 Lisp 構文に近づけるために、読者にこれを回避させる方法も示されました。ただし、これらはどちらも良い考えではないため、これは問題です。一般に、ある言語を別の言語に近づけるための非慣用的なコーディングは、それほど良いアイデアではないことが判明しています。

ただし、実際にセマンティクスを探している場合は、前述のようにプレフィックス バージョンを既に取得していますが、ポストフィックス バージョンを構文的に一致させるのは容易ではありません。十分な数のリーダー ハッカーでそれを実行できますが、見栄えはよくありません。

それがあなたが探しているものなら、私は、a)慣用的でうまく機能するので、incf/decf名に固執し、b)post-incf、post-decfバージョンを書くことをお勧めします。例: (defmacro post-incf (x) `(prog1 ,x (incf ,x)) 種類のもの。

個人的には、これがどのように特に役立つかわかりませんが、ymmv.

于 2008-09-16T22:09:07.627 に答える
5

プレインクリメントの場合、すでに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+)
于 2008-09-16T21:52:09.273 に答える
2

サイモンが彼の投稿にコメントしたことは間違いなく覚えていますが、 user10029アプローチはまだ試す価値があると本当に思っているので、楽しみのために、受け入れられた答えと組み合わせてみました++x演算子を機能させます (つまり、x の値を 1 増やします)。試してみる!

説明: 古き良き SBCL は彼のバージョンをコンパイルしませんでした。なぜなら、'+' 記号は を使用してディスパッチ char ルックアップ テーブルに明示的に設定する必要が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|docstringを参照してください。(密接に) 関連するドキュメントは次の場所にあります。

この実装の結果として、+123 のような数値エントリは理解されなくなります (デバッガーは でジャンプしますno dispatch function defined for #\Newline) が、さらなる回避策 (または回避することさえ) は合理的と思われます: それでもこれに固執したい場合は、おそらく最良の選択は取らないことです++ をプレフィックスとして、ただし ## またはその他の DSL っぽいソリューション

乾杯!

アンドレス

于 2016-03-20T15:25:07.970 に答える
-2

これでうまくいくはずですが、私は Lisp の第一人者ではありません。

(defmacro ++ (variable)
  `(setq ,variable (+ ,variable 1)))
于 2008-09-15T18:54:32.940 に答える