Mon, 24 Oct 2022 09:41:43 +0300
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), } }