common-lispsetf

How to write a multf function in common lisp


I'm looking for a way to modify a property value in a property list by multiplying it with a given factor similar to using incf for adding to a value.

With incf I could say:

(let ((seq '(:x 10 :y 3)))
 (incf (getf seq :y) 3)
 seq)

-> (:x 10 :y 5)

Using a macro I could obtain the result in the following way, but this uses getf twice:

(defmacro multf (place val)
  `(setf ,place (* ,place ,val)))

(let ((seq '(:x 10 :y 3)))
  (multf (getf seq :y) 2)
  seq)

-> (:x 10 :y 6)

How would I do this so that I could obtain the same result using getf only once?

Maybe there are packages with this functionality, but I couldn't find it in the net. Any help is appreciated! This is no homework, I'm just trying to optimize my code and I'm curious to better understand the language. I read about setf-expanders and compiler-macros, but I don't know whether they apply here and how to make use of them.


Solution

  • but this uses getf twice

    the first is a SETF form and the second one is a getter. The first one will be expanded by SETF.

    A short definition of multf using define-modify-macro might be:

    CL-USER 28 > (define-modify-macro multf (&optional (number 1)) *)
    MULTF
    
    CL-USER 29 > (let ((seq '(:x 10 :y 3)))
                   (multf (getf seq :y) 2)
                   seq)
    (:X 10 :Y 6)
    

    The full expansion in LispWorks looks like this:

    (LET ((SEQ '(:X 10 :Y 3)))
      (LET* ((#:G1158 :Y))
        (LET* ()
          (LET ((#:G1157 (* (GETF SEQ #:G1158) 2)))
            (LET ((#:|Store-Var-1156| (SYSTEM::PRIMITIVE-PUTF SEQ #:G1158 #:G1157)))
              (SETQ SEQ #:|Store-Var-1156|)
              #:G1157))))
      SEQ)