Lispworks の HyperspecとCLtL2 ( を探してください) の両方によれば、関数は(関数またはマクロに対する)シンボル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
、その良い例です。しかし一方で、あなたの実装も同様に優れているようです。