38345ccf034a

Iterate harder.
[view raw] [browse files]
author Steve Losh <steve@stevelosh.com>
date Sat, 07 Oct 2017 15:45:58 -0400
parents 9cbd4c08480e
children 15e8fe451b4a
branches/tags (none)
files src/problems.lisp src/utils.lisp

Changes

--- a/src/problems.lisp	Sat Oct 07 13:52:05 2017 -0400
+++ b/src/problems.lisp	Sat Oct 07 15:45:58 2017 -0400
@@ -1893,9 +1893,9 @@
                                previous-digits current-digits)))
            (run-clock (seed)
              (iterate
-               (for current :in-lists (list (mapcar (rcurry #'digits :from-end t)
-                                                    (digital-roots seed))
-                                            '(nil))) ; final turn-off
+               (for current :in-list (mapcar (rcurry #'digits :from-end t)
+                                             (digital-roots seed))
+                    :finally nil)
                (for prev :previous current :initially nil)
                (summing (transition #'transition-sam prev current) :into sam)
                (summing (transition #'transition-max prev current) :into max)
@@ -2222,8 +2222,6 @@
 (test p61 (is (= 28684 (problem-61))))
 (test p62 (is (= 127035954683 (problem-62))))
 (test p63 (is (= 49 (problem-63))))
-
-
 (test p74 (is (= 402 (problem-74))))
 (test p79 (is (= 73162890 (problem-79))))
 (test p92 (is (= 8581146 (problem-92))))
--- a/src/utils.lisp	Sat Oct 07 13:52:05 2017 -0400
+++ b/src/utils.lisp	Sat Oct 07 15:45:58 2017 -0400
@@ -1,15 +1,45 @@
 (in-package :euler)
 
-(defmacro-driver (FOR var ITERATING function SEED value)
+(defmacro-driver (FOR var ITERATING function SEED value &optional
+                  INCLUDE-SEED include-seed?)
+  "Iterate `var` over the series `(f seed), (f (f seed)), (f (f (f seed))), ...`.
+
+  If `include-seed` is given the series will start with the seed itself first.
+
+  Examples:
+
+    (iterate (for n :iterating #'digital-sum :seed 10123456789)
+             (collect n)
+             (until (< n 10)))
+    ; => (46 10 1)
+
+    (iterate (for n :iterating #'digital-sum :seed 10123456789 :include-seed t)
+             (collect n)
+             (until (< n 10)))
+    ; => (10123456789 46 10 1)
+
+  "
   (let ((kwd (if generate 'generate 'for)))
     (with-gensyms (f)
       `(progn
          (with ,f = ,function)
          (,kwd ,var
-          :initially (funcall ,f ,value)
+          :initially ,(if include-seed?
+                        value
+                        `(funcall ,f ,value))
           :then (funcall ,f ,var))))))
 
 (defmacro-driver (FOR var IN-LOOPING list)
+  "Iterate `var` over `list`, looping when the end is reached.
+
+  Example:
+
+    (iterate (for x :in-looping '(1 2 3))
+             (repeat 5)
+             (collect x))
+    ; => (1 2 3 1 2)
+
+  "
   (let ((kwd (if generate 'generate 'for)))
     (with-gensyms (l remaining)
       `(progn
@@ -22,6 +52,14 @@
                     ,remaining)))))))
 
 (defmacro-driver (FOR var KEY function &sequence)
+  "Iterate `var` numerically as with `FOR`, applying `function` to the numbers.
+
+  Example:
+
+    (iterate (for x :key #'evenp :from 0 :to 8) (collect x))
+    ; => (T NIL T NIL T NIL T NIL T)
+
+  "
   (let ((kwd (if generate 'generate 'for)))
     (with-gensyms (i f)
       `(progn
@@ -29,6 +67,57 @@
          (generate ,i ,@(losh::expand-iterate-sequence-keywords))
          (,kwd ,var :next (funcall ,f (next ,i)))))))
 
+(defmacro-driver (FOR var IN-LIST list &optional
+                  BY (step-function '#'cdr)
+                  INITIALLY (initial-value nil initial-value?)
+                  FINALLY (final-value nil final-value?))
+  "Iterate `var` over `list` like vanilla `FOR var IN`, but with more options.
+
+  If `initially` is given, `var` will be bound to it on the first iteration,
+  before proceeding to iterate over the list.
+
+  If `finally` is given, `var` will be bound to it on one more iteration after
+  the end of the list has been reached.
+
+  Examples:
+
+    (iterate (for x :in-list '(1 2 3))
+             (collect x))
+    ; => (1 2 3)
+
+    (iterate (for x :in-list '(1 2 3) :initially 0)
+             (collect x))
+    ; => (0 1 2 3)
+
+    (iterate (for x :in-list '(1 2 3) :finally 4)
+             (collect x))
+    ; => (1 2 3 4)
+
+    (iterate (for x :in-list '(1 2 3) :initially 0 :finally 4)
+             (collect x))
+    ; => (0 1 2 3 4)
+
+  "
+  (let ((kwd (if generate 'generate 'for)))
+    (with-gensyms (l i f done)
+      `(progn
+         (with ,l = ,list)
+         ,@(when initial-value?
+             `((with ,i = ,initial-value)))
+         ,@(when final-value?
+             `((with ,f = ,final-value)
+               (with ,done = nil)))
+         (,kwd ,var :next
+          (cond
+            ,@(when initial-value?
+               `(((first-time-p) ,i)))
+            ,@(when final-value?
+               `((,done (terminate))))
+            ((atom ,l)
+             ,@(if final-value?
+                 `((setf ,done t) ,f)
+                 `((terminate))))
+            (t (prog1 (car ,l) (setf ,l (funcall ,step-function ,l))))))))))
 
 (defmacro-driver (FOR var IN-DIGITS-OF integer &optional RADIX (radix 10))
   "Iterate `var` through the digits of `integer` in base `radix`, low-order first."