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