Allow step closure of AlgIterators to indicate succesfull termination or failure.

Mon, 24 Oct 2022 09:41:43 +0300

author
Tuomo Valkonen <tuomov@iki.fi>
date
Mon, 24 Oct 2022 09:41:43 +0300
changeset 3
20db884b7028
parent 2
ac84e995e119
child 4
61b068c50e25

Allow step closure of AlgIterators to indicate succesfull termination or failure.

src/iterate.rs file | annotate | diff | comparison | revisions
--- a/src/iterate.rs	Sat Oct 22 22:28:04 2022 +0300
+++ b/src/iterate.rs	Mon Oct 24 09:41:43 2022 +0300
@@ -53,12 +53,13 @@
 use cpu_time::ProcessTime;
 use std::marker::PhantomData;
 use std::time::Duration;
+use std::error::Error;
 use crate::types::*;
 use crate::logger::*;
 
 /// Create the displayed presentation for log items. Override for nice (condensed) presentation of rich log items, or define `show` if it is not defined for your type.
 pub trait LogRepr : Debug {
-    fn logrepr(&self) -> ColoredString { format!("« {:?} »", self).as_str().into() }
+    fn logrepr(&self) -> ColoredString { format!("« {self:?} »").as_str().into() }
 }
 
 impl LogRepr for str {
@@ -70,7 +71,7 @@
 }
 
 impl<T> LogRepr for T where T : Num {
-    fn logrepr(&self) -> ColoredString { format!("J={}", self).as_str().into() }
+    fn logrepr(&self) -> ColoredString { format!("J={self}").as_str().into() }
 }
 
 impl<V> LogRepr for Option<V> where V : LogRepr {
@@ -107,30 +108,54 @@
 
 /// State of an [`AlgIterator`]
 pub trait AlgIteratorState {
-    fn if_verbose<A>(&self, calc_objective : impl FnMut() -> A) -> Option<A>;
+    /// Call `call_objective` if this is a verbose iteration.
+    ///
+    /// Verbosity depends on the [`AlgIterator`] that produced this state.
+    ///
+    /// The closure `calc_objective` should return an arbitrary value of type `V`, to be inserted
+    /// into the log, or whatever is deemed by the [`AlgIterator`]. For usage instructions see the
+    /// [module documentation][self].
+    fn if_verbose<V, E : Error>(&self, calc_objective : impl FnMut() -> V) -> Step<V, E>;
 
     fn iteration(&self) -> usize;
 }
 
 /// Result of a step of an [`AlgIterator`]
 #[derive(Debug, Serialize)]
-pub enum Step<V> {
+pub enum Step<V, Fail : Error = std::convert::Infallible> {
     /// Iteration should be terminated
     Terminated,
+    /// Iteration should be terminated due to failure
+    Failure(Fail),
     /// No result this iteration (due to verbosity settings)
     Quiet,
     /// Result of this iteration (due to verbosity settings)
     Result(V),
 }
 
+impl<V, E : Error> Step<V, E> {
+    /// Maps the value contained within the `Step`, if any, by the closure `f`.
+    pub fn map<U>(self, mut f : impl FnMut(V) -> U) -> Step<U, E> {
+        match self {
+            Step::Result(v) =>  Step::Result(f(v)),
+            Step::Failure(e) => Step::Failure(e),
+            Step::Quiet =>      Step::Quiet,
+            Step::Terminated => Step::Terminated,
+        }
+    }
+}
+
 /// An iterator for algorithms, produced by [`AlgIteratorFactory.prepare`].
-/// For usage instructions see the help for [`AlgIteratorFactory`].
-pub trait AlgIterator<V> : Sized {
+///
+/// Typically not accessed directly, but transparently produced by an [`AlgIteratorFactory`].
+/// Every [`AlgIteratorFactory`] has to implement a corresponding `AlgIterator`.
+pub trait AlgIterator : Sized {
     type State : AlgIteratorState;
     type Output;
+    type Err : Error;
 
     /// Advance the iterator.
-    fn step(&mut self) -> Step<Self::Output>;
+    fn step(&mut self) -> Step<Self::Output, Self::Err>;
 
     /// Return current iteration count.
     fn iteration(&self) -> usize {
@@ -140,38 +165,44 @@
     /// Return current iteration stats.
     fn state(&self) -> Self::State;
 
-    /// Iterate the function `step`. It should accept one `state` ([`AlgIteratorState`] parameter,
-    /// and return the output of `state.if_verbose`.
-    /// Typically called through [`AlgIteratorFactory::iterate`].
+    /// Iterate the `AlgIterator` until termination.
+    ///
+    /// Returns either `()` or an error if the step closure terminated in [`Step::Failure´].
     #[inline]
-    fn iterate(&mut self) {
+    fn iterate(&mut self) -> Result<(), Self::Err> {
         loop {
-            if let Step::Terminated = self.step() {
-                break
+            match self.step() {
+                Step::Terminated => return Ok(()),
+                Step::Failure(e) => return Err(e),
+                _ => {},
             }
         }
     }
 
-    /// Converts the `AlgIterator` into a plain [`Iterator`], discarding [`Step::Quiet`] results.
+    /// Converts the `AlgIterator` into a plain [`Iterator`].
+    ///
+    /// [`Step::Quiet`] results are discarded.
     /// (This is to avoid having to manually implement [`IntoIterator`] or [`Iterator`]
     /// for every single [`AlgIterator`].)
-    fn downcast(self) -> AlgIteratorI<Self, V> {
-        AlgIteratorI(self, PhantomData)
+    fn downcast(self) -> AlgIteratorI<Self> {
+        AlgIteratorI(self)
     }
 }
 
 /// Conversion of an `AlgIterator` into a plain [`Iterator`].
-/// It discards [`Step::Quiet`] results.
-pub struct AlgIteratorI<A, V>(A, PhantomData<V>);
+///
+/// The conversion discards [`Step::Quiet`] and panics on [`Step::Failure`].
+pub struct AlgIteratorI<A>(A);
 
-impl<A, V> Iterator for AlgIteratorI<A, V>
-where A : AlgIterator<V> {
+impl<A> Iterator for AlgIteratorI<A>
+where A : AlgIterator {
     type Item = A::Output;
 
     fn next(&mut self) -> Option<A::Output> {
         loop {
             match self.0.step() {
                 Step::Result(v) => return Some(v),
+                Step::Failure(e) => panic!("{e:?}"),
                 Step::Terminated => return None,
                 Step::Quiet => continue,
             }
@@ -193,48 +224,87 @@
 /// })
 /// ```
 pub trait AlgIteratorFactory<V> : Sized {
-    type Iter<F> : AlgIterator<V, State = Self::State, Output = Self::Output>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> : AlgIterator<State = Self::State, Output = Self::Output, Err = E>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type State : AlgIteratorState;
     type Output;
 
     /// Prepare an [`AlgIterator`], consuming the factory.
+    ///
     /// The function `step_fn` should accept a `state` ([`AlgIteratorState`] parameter, and return
     /// a [`Step`].
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V>;
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error;
 
-    /// Iterate the function `step`. It should accept one `state` ([`AlgIteratorState`] parameter,
-    /// and return the output of `state.if_verbose`. For usage instructions see the
-    /// [module documentation][self].
+    /// Iterate the the closure `step`.
+    ///
+    /// The closure should accept one `state` ([`AlgIteratorState`] parameter,
+    /// and return the output of `state.if_verbose`, [`Step::Terminated`] to indicate completion
+    /// for other reason, or [`Step::Failure`] for termination for failure.
+    /// For usage instructions see the [module documentation][self].
+    ///
+    /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`].
+    #[inline]
+    fn iterate_fallible<F, E>(self, step : F) -> Result<(), E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
+        self.prepare(step).iterate()
+    }
+
+    /// Iterate the the closure `step`.
+    ///
+    /// The closure should accept one `state` ([`AlgIteratorState`] parameter,
+    /// and return the output of `state.if_verbose`. For a fallible closure,
+    /// use [`Self::iterate_fallible`].
+    ///
+    /// For usage instructions see the [module documentation][self].
     ///
-    /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator.iterate`].
+    /// This method is equivalent to [`Self::prepare`] followed by [`AlgIterator::iterate`]
+    /// with the error type `E=`[`std::convert::Infallible`].
     #[inline]
-    fn iterate<F>(self, mut step : F) -> () where F : FnMut(&Self::State) -> Option<V> {
+    fn iterate<F>(self, step : F)
+    where F : FnMut(&Self::State) -> Step<V> {
+        self.iterate_fallible(step).unwrap_or_default()
+    }
+
+    /// Iterate the closure `step` with data produced by `datasource`.
+    ///
+    /// The closure should accept a `state` ([`AlgIteratorState`] parameter, and a data parameter
+    /// taken from `datasource` It should return the output of
+    /// `state.[if_verbose][AlgIteratorState.if_verbose]`, [`Step::Terminated`] to indicate
+    /// completion for other reason, or [`Step::Failure`] for termination for failure.
+    ///
+    /// If the `datasource` runs out of data, the iterator is considered having terminated
+    /// successsfully.
+    ///
+    /// For usage instructions see the [module documentation][self].
+    #[inline]
+    fn iterate_data_fallible<F, D, I, E>(self, mut datasource : I, mut step : F) -> Result<(), E>
+    where F : FnMut(&Self::State, D) -> Step<V, E>,
+          I : Iterator<Item = D>,
+          E : Error {
         self.prepare(move |state| {
-            match step(state) {
-                None => Step::Quiet,
-                Some(v) => Step::Result(v),
-            }
+            datasource.next().map_or(Step::Terminated, |d| step(state, d))
         }).iterate()
     }
 
-    /// Iterate the function `step`. It should accept a `state` ([`AlgIteratorState`] parameter,
-    /// and a data parameter taking frmo `datasource`, and return the output of `state.if_verbose`.
+    /// Iterate the closure `step` with data produced by `datasource`.
+    ///
+    /// The closure should accept a `state` ([`AlgIteratorState`] parameter, and a data parameter
+    /// taken from `datasource` It should return the output of
+    /// `state.[if_verbose][AlgIteratorState.if_verbose]`.
+    ///
+    /// If the `datasource` runs out of data, the iterator is considered having terminated
+    /// successsfully.
+    ///
     /// For usage instructions see the [module documentation][self].
     #[inline]
-    fn iterate_data<F, D, I>(self, mut datasource : I, mut step : F)
-    where F : FnMut(&Self::State, D) -> Option<V>,
+    fn iterate_data<F, D, I>(self, datasource : I, step : F)
+    where F : FnMut(&Self::State, D) -> Step<V>,
           I : Iterator<Item = D> {
-        self.prepare(move |state| {
-            match datasource.next() {
-                None => Step::Terminated,
-                Some(d) => match step(state, d) {
-                    None => Step::Quiet,
-                    Some(v) => Step::Result(v),
-                }
-            }
-        }).iterate()
+        self.iterate_data_fallible(datasource, step).unwrap_or_default()
     }
 
     // fn make_iterate<'own>(self)
@@ -370,15 +440,16 @@
 #[derive(Clone,Debug)]
 pub struct BasicAlgIteratorFactory<V> {
     options : AlgIteratorOptions,
-    phantoms : PhantomData<V>,
+    _phantoms : PhantomData<V>,
 }
 
 /// The simplest [`AlgIterator`], reated by [`BasicAlgIteratorFactory`]
 #[derive(Clone,Debug)]
-pub struct BasicAlgIterator<F> {
+pub struct BasicAlgIterator<F, V, E : Error> {
     options : AlgIteratorOptions,
     iter : usize,
     step_fn : F,
+    _phantoms : PhantomData<(V, E)>,
 }
 
 impl AlgIteratorOptions {
@@ -388,7 +459,7 @@
     pub fn instantiate<V>(&self) -> BasicAlgIteratorFactory<V> {
         BasicAlgIteratorFactory {
             options : self.clone(),
-            phantoms : PhantomData
+            _phantoms : PhantomData
         }
     }
 }
@@ -396,12 +467,20 @@
 impl<V> AlgIteratorFactory<V> for AlgIteratorOptions
 where V : LogRepr {
     type State = BasicState;
-    type Iter<F> = BasicAlgIterator<F> where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = BasicAlgIterator<F, V, E>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type Output = V;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
-        BasicAlgIterator{ options : self, iter : 0, step_fn }
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
+        BasicAlgIterator{
+            options : self,
+            iter : 0,
+            step_fn,
+            _phantoms : PhantomData,
+        }
     }
 
     #[inline]
@@ -413,12 +492,20 @@
 impl<V> AlgIteratorFactory<V> for BasicAlgIteratorFactory<V>
 where V : LogRepr {
     type State = BasicState;
-    type Iter<F> = BasicAlgIterator<F> where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = BasicAlgIterator<F, V, E>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type Output = V;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
-        BasicAlgIterator{ options : self.options, iter : 0, step_fn }
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
+        BasicAlgIterator {
+            options : self.options,
+            iter : 0,
+            step_fn,
+            _phantoms : PhantomData
+        }
     }
 
     #[inline]
@@ -427,14 +514,16 @@
     }
 }
 
-impl<V, F> AlgIterator<V> for BasicAlgIterator<F>
+impl<F, V, E> AlgIterator for BasicAlgIterator<F, V, E>
 where V : LogRepr,
-      F : FnMut(&BasicState) -> Step<V> {
+      E : Error,
+      F : FnMut(&BasicState) -> Step<V, E> {
     type State = BasicState;
     type Output = V;
+    type Err = E;
 
     #[inline]
-    fn step(&mut self) -> Step<V> {
+    fn step(&mut self) -> Step<V, E> {
         if self.iter >= self.options.max_iter {
             Step::Terminated
         } else {
@@ -473,11 +562,11 @@
 
 impl AlgIteratorState for BasicState {
     #[inline]
-    fn if_verbose<A>(&self, mut calc_objective : impl FnMut() -> A) -> Option<A> {
+    fn if_verbose<V, E : Error>(&self, mut calc_objective : impl FnMut() -> V) -> Step<V, E> {
         if self.calc {
-            Some(calc_objective())
+            Step::Result(calc_objective())
         } else {
-            None
+            Step::Quiet
         }
     }
 
@@ -512,13 +601,15 @@
 for StallIteratorFactory<U, BaseFactory>
 where BaseFactory : AlgIteratorFactory<V, Output=U>,
       U : SignedNum + PartialOrd {
-    type Iter<F> = StallIterator<U, BaseFactory::Iter<F>>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = StallIterator<U, BaseFactory::Iter<F, E>>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type State = BaseFactory::State;
     type Output = BaseFactory::Output;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
         StallIterator {
             base_iterator : self.base_options.prepare(step_fn),
             stall : self.stall,
@@ -531,15 +622,16 @@
     }
 }
 
-impl<V, U, BaseIterator> AlgIterator<V>
+impl<U, BaseIterator> AlgIterator
 for StallIterator<U, BaseIterator>
-where BaseIterator : AlgIterator<V, Output=U>,
+where BaseIterator : AlgIterator<Output=U>,
       U : SignedNum + PartialOrd {
     type State = BaseIterator::State;
     type Output = BaseIterator::Output;
+    type Err = BaseIterator::Err;
 
     #[inline]
-    fn step(&mut self) -> Step<U> {
+    fn step(&mut self) -> Step<U, Self::Err> {
         match self.base_iterator.step() {
             Step::Result(nv) => {
                 let previous_v = self.previous_value;
@@ -584,13 +676,15 @@
 for ValueIteratorFactory<U, BaseFactory>
 where BaseFactory : AlgIteratorFactory<V, Output=U>,
       U : SignedNum + PartialOrd {
-    type Iter<F> = ValueIterator<U, BaseFactory::Iter<F>>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = ValueIterator<U, BaseFactory::Iter<F, E>>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type State = BaseFactory::State;
     type Output = BaseFactory::Output;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
         ValueIterator {
             base_iterator : self.base_options.prepare(step_fn),
             target : self.target
@@ -602,15 +696,16 @@
     }
 }
 
-impl<V, U, BaseIterator> AlgIterator<V>
+impl<U, BaseIterator> AlgIterator
 for ValueIterator<U, BaseIterator>
-where BaseIterator : AlgIterator<V, Output=U>,
+where BaseIterator : AlgIterator<Output=U>,
       U : SignedNum + PartialOrd {
     type State = BaseIterator::State;
     type Output = BaseIterator::Output;
+    type Err = BaseIterator::Err;
 
     #[inline]
-    fn step(&mut self) -> Step<U> {
+    fn step(&mut self) -> Step<U, Self::Err> {
         match self.base_iterator.step() {
             Step::Result(v) => {
                  if v <= self.target {
@@ -673,12 +768,14 @@
 where BaseFactory : AlgIteratorFactory<V>,
       BaseFactory::Output : 'log {
     type State = BaseFactory::State;
-    type Iter<F> = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter<F>>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter<F, E>>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type Output = ();
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
         LoggingIterator {
             base_iterator : self.base_options.prepare(step_fn),
             logger : self.logger,
@@ -691,15 +788,16 @@
     }
 }
 
-impl<'log, V, BaseIterator> AlgIterator<V>
+impl<'log, BaseIterator> AlgIterator
 for LoggingIterator<'log, BaseIterator::Output, BaseIterator>
-where BaseIterator : AlgIterator<V>,
+where BaseIterator : AlgIterator,
       BaseIterator::Output : 'log {
     type State = BaseIterator::State;
     type Output = ();
+    type Err = BaseIterator::Err;
 
     #[inline]
-    fn step(&mut self) -> Step<Self::Output> {
+    fn step(&mut self) -> Step<Self::Output, Self::Err> {
         match self.base_iterator.step() {
             Step::Result(v) => {
                 self.logger.log(v);
@@ -707,6 +805,7 @@
             },
             Step::Quiet => Step::Quiet,
             Step::Terminated => Step::Terminated,
+            Step::Failure(e) => Step::Failure(e),
         }
     }
 
@@ -740,12 +839,14 @@
 where BaseFactory : AlgIteratorFactory<V>,
       G : Fn(usize, BaseFactory::Output) -> U {
     type State = BaseFactory::State;
-    type Iter<F> = MappingIterator<G, BaseFactory::Iter<F>>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = MappingIterator<G, BaseFactory::Iter<F, E>>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type Output = U;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
         MappingIterator {
             base_iterator : self.base_options.prepare(step_fn),
             map : self.map
@@ -758,19 +859,21 @@
     }
 }
 
-impl<V, U, G, BaseIterator> AlgIterator<V>
+impl<U, G, BaseIterator> AlgIterator
 for MappingIterator<G, BaseIterator>
-where BaseIterator : AlgIterator<V>,
+where BaseIterator : AlgIterator,
       G : Fn(usize, BaseIterator::Output) -> U {
     type State = BaseIterator::State;
     type Output = U;
+    type Err = BaseIterator::Err;
 
     #[inline]
-    fn step(&mut self) -> Step<Self::Output> {
+    fn step(&mut self) -> Step<Self::Output, Self::Err> {
         match self.base_iterator.step() {
             Step::Result(v) => Step::Result((self.map)(self.iteration(), v)),
             Step::Quiet => Step::Quiet,
             Step::Terminated => Step::Terminated,
+            Step::Failure(e) => Step::Failure(e),
         }
     }
 
@@ -819,12 +922,14 @@
 for TimingIteratorFactory<BaseFactory>
 where BaseFactory : AlgIteratorFactory<V> {
     type State = BaseFactory::State;
-    type Iter<F> = TimingIterator<BaseFactory::Iter<F>>
-        where F : FnMut(&Self::State) -> Step<V>;
+    type Iter<F, E> = TimingIterator<BaseFactory::Iter<F, E>>
+        where F : FnMut(&Self::State) -> Step<V, E>,
+              E : Error;
     type Output = Timed<BaseFactory::Output>;
 
-    fn prepare<F>(self, step_fn : F) -> Self::Iter<F>
-    where F : FnMut(&Self::State) -> Step<V> {
+    fn prepare<F, E>(self, step_fn : F) -> Self::Iter<F, E>
+    where F : FnMut(&Self::State) -> Step<V, E>,
+          E : Error {
         TimingIterator {
             base_iterator : self.0.prepare(step_fn),
             start_time : ProcessTime::now()
@@ -837,14 +942,15 @@
     }
 }
 
-impl<V, BaseIterator> AlgIterator<V>
+impl<BaseIterator> AlgIterator
 for TimingIterator<BaseIterator>
-where BaseIterator : AlgIterator<V> {
+where BaseIterator : AlgIterator {
     type State = BaseIterator::State;
     type Output = Timed<BaseIterator::Output>;
+    type Err = BaseIterator::Err;
 
     #[inline]
-    fn step(&mut self) -> Step<Self::Output> {
+    fn step(&mut self) -> Step<Self::Output, Self::Err> {
         match self.base_iterator.step() {
             Step::Result(data) => {
                 Step::Result(Timed{
@@ -854,6 +960,7 @@
             },
             Step::Quiet => Step::Quiet,
             Step::Terminated => Step::Terminated,
+            Step::Failure(e) => Step::Failure(e),
         }
     }
 

mercurial