问题描述
我需要一个incf
函数,该函数会在增量过程中进行一些边界检查:
I need an incf
function which does some bounds checking during the increment:
val := val + delta
if val >= 1.0
then return 1.0
else return val
我可以使用incf
来写:
(defun incf-bounded(val delta)
(incf val delta)
(if (>= val 1.0) 1.0 val))
在这种情况下,我需要像(setf x (incf-bounded x delta))
这样使用它.但是我该如何写一个像(incf-bounded x delta)
这样可以使用的东西,即x
将在哪里修改?
In such case I need to use this like (setf x (incf-bounded x delta))
. But how do I write one which I can use like (incf-bounded x delta)
, i.e., where x
will be modified?
推荐答案
这是 define-modify-macro
(也已对此进行了描述在Lisp中添加作为推送是不利因素吗?,但目前的情况较为简单).首先,将有界和写为一个函数.这很简单;如果val
和delta
的总和大于1.0
,则返回1.0
,否则返回.根据您发布的伪代码和Lisp代码,可能是:
This is a good use case for define-modify-macro
(which has also been described in what is to append as push is to cons, in Lisp?, but the present case is simpler). First, write your bounded sum as a function. This is pretty straightforward; it takes val
and delta
and returns 1.0
if their sum is greater than 1.0
, and their sum otherwise. Based on the pseudo code and Lisp code you posted, this could be:
(defun sum-bounded (val delta)
(if (>= (+ val delta) 1.0)
1.0
(+ val delta)))
实际上,仅对于计算此值,您可以使用:
Actually, for just computing this value, you can use:
(defun sum-bounded (val delta)
(min 1.0 (+ val delta)))
现在使用define-modify-macro
定义宏incf-bounded
:
(define-modify-macro incf-bounded (delta) sum-bounded)
宏将 place 作为其第一个参数,将delta作为第二个参数.它安全地从该位置检索值,使用该值和增量计算sum-bounded
,然后将结果存储回 place . 安全"是指它避免了多次评估的可能问题,如 Lars Brinkhoff明智地警告.然后,您只需使用它即可:
The macro takes a place as its first argument and delta as a second. It safely retrieves the value from the place, computes sum-bounded
with that value and delta, and then stores the result back into the place. "Safely" here means that it avoids possible problems with multiple evaluation, as Lars Brinkhoff's wisely warns against. Then you just use it:
(let ((x .5))
(incf-bounded x .3)
(print x) ; prints 0.8
(incf-bounded x .3)
(print x)) ; prints 1.0 (not 1.1)
在更复杂的情况下,要修改的 place 自然不是您想要的宏的第一个参数,您需要编写自己的宏并使用 get-setf-expansion
,但这在
For more complicated cases where the place that would be modified isn't naturally the first argument to the macro that you want, you'd need to write your own macro and use get-setf-expansion
, but this is explained in more detail in
(defun sum-bounded (val delta)
"Returns the lesser of 1.0 or the sum of val and delta."
(min 1.0 (+ val delta)))
(define-modify-macro incf-bounded (delta) sum-bounded
"(incf-bounded place delta) computes the sum of the value of the
place and delta, and assigns the lesser of 1.0 and the sum of the value
and delta to place.")
(defun demo ()
(let ((x .5))
(incf-bounded x .3)
(print x) ; prints 0.8
(incf-bounded x .3)
(print x))) ; prints 1.0 (not 1.1)
这篇关于编写破坏性宏或类似incf的函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!