src/mutation.lisp @ 3cddaf4f6564

Add `initial-value` and `modulo` to `summation` and `product`
author Steve Losh <steve@stevelosh.com>
date Wed, 07 Nov 2018 18:08:28 -0500
parents de9d10a9b4b5
children a8b03be59283
(in-package :losh.mutation)

(eval-when (:compile-toplevel :load-toplevel :execute)
  (defun build-zap (place expr env)
    (multiple-value-bind (temps exprs stores store-expr access-expr)
        (get-setf-expansion place env)
      `(let* (,@(mapcar #'list temps exprs)
              (,(car stores) (symbol-macrolet ((% ,access-expr))
                               ,expr)))
         ,store-expr))))

(defmacro zapf (&rest place-expr-pairs &environment env)
  "Update each `place` by evaluating `expr` with `%` bound to the current value.

  `zapf` works like `setf`, but when evaluating the value expressions the symbol
  `%` will be bound to the current value of the place.

  Examples:

    (zapf foo (1+ %)
          (car bar) (if (> % 10) :a :b))

  "
  ;; original idea/name from http://malisper.me/2015/09/29/zap/
  `(progn
    ,@(loop :for (place expr . nil) :on place-expr-pairs :by #'cddr
            :collect (build-zap place expr env))))


(define-modify-macro mulf (factor) *
  "Multiply `place` by `factor` in-place.")


(defun %divf (value &optional divisor)
  (if divisor
    (/ value divisor)
    (/ value)))

(define-modify-macro divf (&optional divisor) %divf
  "Divide `place` by `divisor` in-place.

  If `divisor` is not given, `place` will be set to `(/ 1 place)`.

  ")


(define-modify-macro modf (divisor) mod
  "Modulo `place` by `divisor` in-place.")

(define-modify-macro remainderf (divisor) rem
  "Remainder `place` by `divisor` in-place.")

(define-modify-macro clampf (from to) clamp
  "Clamp `place` between `from` and `to` in-place.")

(define-modify-macro negatef () -
  "Negate the value of `place`.")

(define-modify-macro notf () not
  "Set `place` to `(not place)` in-place.")


(defun %funcall (value function)
  (funcall function value))

(define-modify-macro %callf (function) %funcall
  "Set `place` to the result of calling `function` on its current value.")


(defmacro callf (&rest place-function-pairs)
  "Set each `place` to the result of calling `function` on its current value.

  Examples:

    (let ((x 10) (y 20))
      (callf x #'1-
             y #'1+)
      (list x y))
    =>
    (9 21)
  "
  `(progn
     ,@(loop :for (place function . nil) :on place-function-pairs :by #'cddr
             :collect `(%callf ,place ,function))))