# HG changeset patch # User Steve Losh # Date 1541632108 18000 # Node ID 3cddaf4f6564fe3581fb05a4370292518781290b # Parent 30253ddf17b78a6c52a3a463a63f9aaebebf71f5 Add `initial-value` and `modulo` to `summation` and `product` diff -r 30253ddf17b7 -r 3cddaf4f6564 losh.asd --- a/losh.asd Fri Jul 13 21:26:04 2018 +0000 +++ b/losh.asd Wed Nov 07 18:08:28 2018 -0500 @@ -46,7 +46,8 @@ (:file "random" :depends-on ("math" "chili-dogs")) (:file "sequences" :depends-on ("chili-dogs" - "hash-tables")) + "hash-tables" + "mutation")) (:file "debugging" :depends-on ("control-flow" "math" "hash-tables")) diff -r 30253ddf17b7 -r 3cddaf4f6564 package.lisp --- a/package.lisp Fri Jul 13 21:26:04 2018 +0000 +++ b/package.lisp Wed Nov 07 18:08:28 2018 -0500 @@ -254,7 +254,8 @@ (:use :cl :iterate :losh.quickutils :losh.chili-dogs :losh.hash-tables - :losh.iterate-pre) + :losh.iterate-pre + :losh.mutation) (:documentation "Utilities for operating on sequences.") (:export :extrema diff -r 30253ddf17b7 -r 3cddaf4f6564 src/sequences.lisp --- a/src/sequences.lisp Fri Jul 13 21:26:04 2018 +0000 +++ b/src/sequences.lisp Wed Nov 07 18:08:28 2018 -0500 @@ -268,11 +268,25 @@ el))))) -(defun-inlineable summation (sequence &key key) +(defmacro doseq ((var sequence) &body body) + "Perform `body` with `var` bound to each element in `sequence` in turn. + + It's like `cl:dolist`, but for all sequences. + + " + `(map nil (lambda (,var) ,@body) ,sequence)) + + +(defun-inlineable summation (sequence &key key (initial-value 0) modulo) "Return the sum of all elements of `sequence`. If `key` is given, it will be called on each element to compute the addend. + If `initial-value` is given, it will be used instead of 0 to seed the addition. + + If `modulo` is given the successive sums will be modulo'ed by it along the + way, which can prevent the need for bignums if you don't need the full result. + This function's ugly name was chosen so it wouldn't clash with iterate's `sum` symbol. Sorry. @@ -288,23 +302,40 @@ ; => 3 " - (if key - (iterate (for n :in-whatever sequence) - (sum (funcall key n))) - (iterate (for n :in-whatever sequence) - (sum n)))) + (let ((result initial-value)) + (when modulo (modf result modulo)) + (if modulo + (if key + (doseq (n sequence) (setf result (mod (+ result (funcall key n)) modulo))) + (doseq (n sequence) (setf result (mod (+ result n) modulo)))) + (if key + (doseq (n sequence) (setf result (+ result (funcall key n)))) + (doseq (n sequence) (setf result (+ result n))))) + result)) -(defun-inlineable product (sequence &key key) +(defun-inlineable product (sequence &key key (initial-value 1) modulo) "Return the product of all elements of `sequence`. If `key` is given, it will be called on each element to compute the multiplicand. + If `initial-value` is given, it will be used instead of 1 to seed the + multiplication. + + If `modulo` is given the successive products will be modulo'ed by it along the + way, which can prevent the need for bignums if you don't need the full result. + Examples: (product #(1 2 3)) ; => 6 + (product #(1 2 3) :modulo 5) + ; => 1 + + (product #(1 2 3) :modulo 5 :initial-value 2) + ; => 2 + (product '(\"1\" \"2\" \"3\") :key #'parse-integer) ; => 6 @@ -312,19 +343,15 @@ ; => 1 " - (if key - (iterate (for n :in-whatever sequence) - (multiplying (funcall key n))) - (iterate (for n :in-whatever sequence) - (multiplying n)))) + (let ((result initial-value)) + (when modulo (modf result modulo)) + (if modulo + (if key + (doseq (n sequence) (setf result (mod (* result (funcall key n)) modulo))) + (doseq (n sequence) (setf result (mod (* result n) modulo)))) + (if key + (doseq (n sequence) (setf result (* result (funcall key n)))) + (doseq (n sequence) (setf result (* result n))))) + result)) -(defmacro doseq ((var sequence) &body body) - "Perform `body` with `var` bound to each element in `sequence` in turn. - - It's like `cl:dolist`, but for all sequences. - - " - `(map nil (lambda (,var) ,@body) ,sequence)) - -