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))(incf-bounded x delta)しかし、 のように使用できるもの、つまり、どこが変更されるものをどのように作成すればxよいでしょうか?

4

2 に答える 2

5

これは(これはwhat is to append as push is to cons, in Lisp?define-modify-macroでも説明されていますが、現在のケースはより単純です) の良い使用例です。まず、制限付き合計を関数として記述します。これは非常に簡単です。と を取り、それらの合計が より大きい場合はを返し、そうでない場合はそれらの合計を返します。あなたが投稿した疑似コードと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)

マクロは最初の引数として場所を取り、2 番目の引数としてデルタを取ります。プレースから安全に値を取得し、sum-boundedその値とデルタを使用して計算し、結果をプレースに格納します。ここで「安全に」とは、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 に答える