gpt4 book ai didi

macros - 如何用任意函数修改地方

转载 作者:太空宇宙 更新时间:2023-11-03 18:35:28 25 4
gpt4 key购买 nike

有时我们需要修改一个place但是这里没有满足我们需求的内置函数。

例如,这里有 incfdecf 用于加法和减法:

CL-USER> (defvar *x* 5)
*X*
CL-USER> (incf *x* 3)
8
CL-USER> *x*
8
CL-USER> (decf *x* 10)
-2
CL-USER> *x*
-2

但是乘法和除法呢?如果我们想用任意函数修改一个地方,就像这样:

(xf (lambda (x) ...) *x*)

xf 实用程序将非常有用,尤其是当我们必须处理深层嵌套结构时:

(my-accessor (aref (cdr *my-data*) n))

最佳答案

使用 define-modify-macro 定义新宏

为我们的需要定义新的方便宏的一种简单方法是 define-modify-macro .这是一个方便的宏,可以为我们创建其他宏。

Syntax:

define-modify-macro name lambda-list function [documentation]

⇒ name

我们应该提供新宏的名称、参数列表(不包括那里的位置)和将用于处理的函数符号。

使用示例:

(define-modify-macro togglef () not
"togglef modifies place, changing nil value to t and non-nil value to nil")

(define-modify-macro mulf (&rest args) *
"mulf modifies place, assigning product to it")

(define-modify-macro divf (&rest args) /
"divf modifies place, assigning result of division to it")

但是,define-modify-macro 不能用于任意处理。在这里,我们必须看看其他可能性。

函数get-setf-expansion

函数get-setf-expansion不创建任何宏,但提供我们可以用来编写自己的信息。

Syntax:

get-setf-expansion place &optional environment

⇒ vars, vals, store-vars, writer-form, reader-form

如您所见,它返回了一堆值,因此乍一看可能会造成混淆。让我们尝试一下示例:

CL-USER> (defvar *array* #(1 2 3 4 5))
*ARRAY*
CL-USER> (get-setf-expansion '(aref *array* 1))
; get-setf-expansion is a function, so we have to quote its argument
(#:G6029 #:G6030) ; list of variables needed to modify place
(*ARRAY* 1) ; values for these variables
(#:G6031) ; variable to store result of calculation
(SYSTEM::STORE #:G6029 ; writer-form: we should run it to modify place
#:G6030 ; ^
#:G6031) ; ^
(AREF #:G6029 #:G6030) ; reader-form: hm.. looks like our expression

编写xf

现在我们似乎已经掌握了编写 xf 宏的所有信息:

(defmacro xf (fn place &rest args &environment env)
(multiple-value-bind (vars forms var set access)
(get-setf-expansion place env)
(let ((g (gensym)))
`(let* ((,g ,fn) ; assign supplied function to generated symbol
,@(mapcar #'list vars forms) ; generate pairs (variable value)
(,(car var) (funcall ,g ,access ,@args))) ; call supplied function
; and save the result, we use reader-form here to get intial value
,set)))) ; just put writer-from here as provided

请注意,xf 宏需要 evironment variable并将其传递给 get-setf-expansion。需要此变量以确保考虑编译环境中建立的任何词法绑定(bind)或定义。

让我们试试看:

CL-USER> (defvar *var* '(("foo" . "bar") ("baz" . "qux")))
*VAR*
CL-USER> (xf #'reverse (cdr (second *var*)))
"xuq"
CL-USER> *var*
(("foo" . "bar") ("baz" . "xuq"))

扩展:

(LET* ((#:G6033 #'REVERSE)
(#:TEMP-6032 (SECOND *VAR*))
(#:NEW-6031 (FUNCALL #:G6033
(CDR #:TEMP-6032))))
(SYSTEM::%RPLACD #:TEMP-6032 #:NEW-6031))

我希望这些信息有用。

此答案基于 Paul Graham's On Lisp部分 12.4 更复杂的实用程序。

关于macros - 如何用任意函数修改地方,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25160147/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com