3

On Lisp のセクション 12.4 で Paul Graham は次のように書いています。_fdefine-modify-macro

しかし、このようなものの何が問題なのですか?

(define-modify-macro _f (op operand)
  (lambda (x op operand)
    (funcall op x operand)))

(let ((lst '(1 2 3)))
  (_f (second lst) #'* 6)
  lst)

=> (1 12 3)

ANSI Common Lisp の define-modify-macro に、On Lisp が書かれた時点では有効でなかった変更が加えられたのでしょうか? または、ここで使用しない理由として記載されている以外の理由がありdefine-modify-macroますか?

Graham は、次のような電話をかけることができるようにしたいと考えているようです。

(_f * (second lst) 6)

それよりも

(_f #'* (second lst) 6)

しかし、それは Common Lisp のような Lisp2 と一致しないのでしょうか?

4

2 に答える 2

2

Lispworks の HyperspecCLtL2 ( を探してください) の両方によれば、関数は(関数またはマクロに対する)シンボルdefine-modify-macroであると見なされます。私の知る限り、次の定義は仕様に準拠していない可能性があります。

(define-modify-macro _f (op operand)
  (lambda (x op operand)
    (funcall op x operand)))

しかしもちろん、実装によってそれが許可される可能性はあります。標準に準拠していることを確認するために、独自の関数やマクロを定義できます。

(defmacro funcall-1 (val fun &rest args)
  `(funcall ,fun ,val ,@args))

(define-modify-macro _ff (&rest args)  funcall-1)

(let ((x (list 1 2 3 4)))
  (_ff (third x) #'+ 10)
  x)

関数を 2 番目の引数として使用する場合は、別のマクロを定義できます。

(defmacro ff (fun-form place &rest args)
  `(_ff ,place ,fun-form ,@args))

基本的に、あなたのアプローチはでラップfuncallする ことで構成されdefine-modify-macro、その関数の引数として目的の関数を与えます。一見、ハックのように見えますが、以下に示すように、これはOn Lispのものと同じマクロ展開されたコードを提供します。後者を少し変更すると仮定します。

上記のマクロ展開は次のとおりです。

(LET ((X (LIST 1 2 3 4)))
  (LET* ((#:G1164 X) (#:G1165 (FUNCALL #'+ (THIRD #:G1164) 10)))
    (SB-KERNEL:%RPLACA (CDDR #:G1164) #:G1165))
  X)

On Lispのバージョンは次のように動作します。

(defmacro _f (op place &rest args)
   (multiple-value-bind (vars forms var set access)
       (get-setf-expansion
        place)
        `(let* (,@(mapcar #'list vars forms)
               (, (car var) (,op ,access ,@args)))
           ,set)))


(let ((x (list 1 2 3 4)))
  (_f * (third x) 10)
  x)

マクロ拡張:

(LET ((X (LIST 1 2 3 4)))
  (LET* ((#:G1174 X) (#:G1175 (* (THIRD #:G1174) 10)))
    (SB-KERNEL:%RPLACA (CDDR #:G1174) #:G1175))
  X)

ここでは、*がマクロ展開によって直接注入されます。これは、結果のコードに実行時のオーバーヘッドが発生する可能性がないことを意味します (ただし、コンパイラはおそらく(funcall #'+ ...)同様に適切に処理します)。マクロに渡す#'+と、マクロ展開に失敗します。これはあなたのアプローチとの大きな違いですが、大きな制限ではありません。On Lispバージョンが を#'*、または(create-closure)演算子としても受け入れるようにするには、次のように変更する必要があります。

 (defmacro _f (op place &rest args)
    (multiple-value-bind (vars forms var set access)
        (get-setf-expansion
         place)
         `(let* (,@(mapcar #'list vars forms)
                (, (car var) (funcall ,op ,access ,@args)))
            ,set)))

(への呼び出しを参照funcall)

前の例は、次のように展開されます#'*

(LET ((X (LIST 1 2 3 4)))
  (LET* ((#:G1180 X) (#:G1181 (FUNCALL #'* (THIRD #:G1180) 10)))
    (SB-KERNEL:%RPLACA (CDDR #:G1180) #:G1181))
  X)

今、それはあなたのバージョンとまったく同じです。On Lisp uses はの使用_f方法を示してget-setf-expansionおり_f、その良い例です。しかし一方で、あなたの実装も同様に優れているようです。

于 2015-07-23T15:01:59.133 に答える
1

*またはを渡すことを好むかどうかという問題については、 のバージョンと@coredump の適応バージョン ( with ) の両方が、 op の位置でラムダ形式を受け入れる#'*ことにも注意してください。前の。define-modify-macro_ffuncall#'(lambda (x y) (* x y))#'(lambda (x y) (* x y))

興味深いことに、Doug Hoyteは彼の著書Let over Lambdaの中で、彼の著書ANSI Common Lisp#'の中で、ラムダ形式の前に the を省略できることは、省略を好む前に「せいぜい優雅さの見掛け倒しの形」を提供するという Graham の発言に注意を向けています。それ。

私はどちらの立場を取っているわけではありません. Graham が を選択したことを考えると_f#'.

于 2015-07-25T13:39:41.263 に答える