3cddaf4f6564

Add `initial-value` and `modulo` to `summation` and `product`
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Wed, 07 Nov 2018 18:08:28 -0500
parents 30253ddf17b7
children aaf09c52cad9
branches/tags (none)
files losh.asd package.lisp src/sequences.lisp

Changes

--- 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"))
--- 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
--- 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))
-
-