3

我需要一个incf在增量期间进行边界检查的函数:

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))。但是我该如何写一个我可以使用的like (incf-bounded x delta),即在哪里x修改呢?

4

2 回答 2

5

这是一个很好的用例(在 Lisp 中,what is to append as push is to cons?define-modify-macro中也有描述,但目前的情况更简单)。首先,将有界总和写成一个函数。这很简单;如果它们的总和大于,则返回and ,否则返回。根据您发布的伪代码和 Lisp 代码,这可能是:valdelta1.01.0

(defun sum-bounded (val delta)
  (if (>= (+ val delta) 1.0)
      1.0
      (+ val delta)))

实际上,仅计算此值,您可以使用:

(defun sum-bounded (val delta)
  (min 1.0 (+ val delta)))

现在你define-modify-macro用来定义一个宏incf-bounded

(define-modify-macro incf-bounded (delta) sum-bounded)

宏将一个位置作为它的第一个参数,将 delta 作为第二个参数。它安全地从 place 检索值,sum-bounded使用该值和 delta 进行计算,然后将结果存储回place。这里的“安全”意味着它避免了多重评估可能出现的问题,正如Lars Brinkhoff 明智地警告的那样。然后你只需使用它:

(let ((x .5))
  (incf-bounded x .3)
  (print x)                             ; prints 0.8
  (incf-bounded x .3)
  (print x))                            ; prints 1.0 (not 1.1)

对于更复杂的情况,要修改的地方自然不是您想要的宏的第一个参数,您需要编写自己的宏并使用get-setf-expansion,但这在中进行了更详细的解释

一起编码以便于复制和粘贴

(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)
于 2013-10-21T14:59:58.330 回答
4

val如果您希望它成为可能产生副作用的地方,您可能需要小心:

(defmacro incf-bounded (form delta &environment env)
  (multiple-value-bind (temps vals vars writer reader)
      (get-setf-expansion form env)
    `(let* (,@(mapcar #'list temps vals)
            (,(first vars) (min (+ ,delta ,reader) 1.0))) ;Edited, see comments.
       ,writer)))

用例如试试

(let ((list (list 0 0.5 1)))
  (loop with i = -1 repeat 3 do (incf-bounded (nth (incf i) list) 0.5))
  list)

(这看起来不必要地复杂,因为我想在 . 的第一个参数中产生副作用incf-bounded。)

于 2013-10-21T05:29:52.170 回答