# HG changeset patch # User Tuomo Valkonen # Date 1666593703 -10800 # Node ID 20db884b7028e2b53e861931d38c080f2278ec08 # Parent ac84e995e119eb4c6f34eba3bdeb55088f7d946b Allow step closure of AlgIterators to indicate succesfull termination or failure. diff -r ac84e995e119 -r 20db884b7028 src/iterate.rs --- 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 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 LogRepr for Option where V : LogRepr { @@ -107,30 +108,54 @@ /// State of an [`AlgIterator`] pub trait AlgIteratorState { - fn if_verbose(&self, calc_objective : impl FnMut() -> A) -> Option; + /// 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(&self, calc_objective : impl FnMut() -> V) -> Step; fn iteration(&self) -> usize; } /// Result of a step of an [`AlgIterator`] #[derive(Debug, Serialize)] -pub enum Step { +pub enum Step { /// 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 Step { + /// Maps the value contained within the `Step`, if any, by the closure `f`. + pub fn map(self, mut f : impl FnMut(V) -> U) -> Step { + 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 : 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; + fn step(&mut self) -> Step; /// 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 { - AlgIteratorI(self, PhantomData) + fn downcast(self) -> AlgIteratorI { + AlgIteratorI(self) } } /// Conversion of an `AlgIterator` into a plain [`Iterator`]. -/// It discards [`Step::Quiet`] results. -pub struct AlgIteratorI(A, PhantomData); +/// +/// The conversion discards [`Step::Quiet`] and panics on [`Step::Failure`]. +pub struct AlgIteratorI(A); -impl Iterator for AlgIteratorI -where A : AlgIterator { +impl Iterator for AlgIteratorI +where A : AlgIterator { type Item = A::Output; fn next(&mut self) -> Option { 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 : Sized { - type Iter : AlgIterator - where F : FnMut(&Self::State) -> Step; + type Iter : AlgIterator + where F : FnMut(&Self::State) -> Step, + 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(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step; + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + 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(self, step : F) -> Result<(), E> + where F : FnMut(&Self::State) -> Step, + 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(self, mut step : F) -> () where F : FnMut(&Self::State) -> Option { + fn iterate(self, step : F) + where F : FnMut(&Self::State) -> Step { + 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(self, mut datasource : I, mut step : F) -> Result<(), E> + where F : FnMut(&Self::State, D) -> Step, + I : Iterator, + 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(self, mut datasource : I, mut step : F) - where F : FnMut(&Self::State, D) -> Option, + fn iterate_data(self, datasource : I, step : F) + where F : FnMut(&Self::State, D) -> Step, I : Iterator { - 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 { options : AlgIteratorOptions, - phantoms : PhantomData, + _phantoms : PhantomData, } /// The simplest [`AlgIterator`], reated by [`BasicAlgIteratorFactory`] #[derive(Clone,Debug)] -pub struct BasicAlgIterator { +pub struct BasicAlgIterator { options : AlgIteratorOptions, iter : usize, step_fn : F, + _phantoms : PhantomData<(V, E)>, } impl AlgIteratorOptions { @@ -388,7 +459,7 @@ pub fn instantiate(&self) -> BasicAlgIteratorFactory { BasicAlgIteratorFactory { options : self.clone(), - phantoms : PhantomData + _phantoms : PhantomData } } } @@ -396,12 +467,20 @@ impl AlgIteratorFactory for AlgIteratorOptions where V : LogRepr { type State = BasicState; - type Iter = BasicAlgIterator where F : FnMut(&Self::State) -> Step; + type Iter = BasicAlgIterator + where F : FnMut(&Self::State) -> Step, + E : Error; type Output = V; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { - BasicAlgIterator{ options : self, iter : 0, step_fn } + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { + BasicAlgIterator{ + options : self, + iter : 0, + step_fn, + _phantoms : PhantomData, + } } #[inline] @@ -413,12 +492,20 @@ impl AlgIteratorFactory for BasicAlgIteratorFactory where V : LogRepr { type State = BasicState; - type Iter = BasicAlgIterator where F : FnMut(&Self::State) -> Step; + type Iter = BasicAlgIterator + where F : FnMut(&Self::State) -> Step, + E : Error; type Output = V; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { - BasicAlgIterator{ options : self.options, iter : 0, step_fn } + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { + BasicAlgIterator { + options : self.options, + iter : 0, + step_fn, + _phantoms : PhantomData + } } #[inline] @@ -427,14 +514,16 @@ } } -impl AlgIterator for BasicAlgIterator +impl AlgIterator for BasicAlgIterator where V : LogRepr, - F : FnMut(&BasicState) -> Step { + E : Error, + F : FnMut(&BasicState) -> Step { type State = BasicState; type Output = V; + type Err = E; #[inline] - fn step(&mut self) -> Step { + fn step(&mut self) -> Step { if self.iter >= self.options.max_iter { Step::Terminated } else { @@ -473,11 +562,11 @@ impl AlgIteratorState for BasicState { #[inline] - fn if_verbose(&self, mut calc_objective : impl FnMut() -> A) -> Option { + fn if_verbose(&self, mut calc_objective : impl FnMut() -> V) -> Step { if self.calc { - Some(calc_objective()) + Step::Result(calc_objective()) } else { - None + Step::Quiet } } @@ -512,13 +601,15 @@ for StallIteratorFactory where BaseFactory : AlgIteratorFactory, U : SignedNum + PartialOrd { - type Iter = StallIterator> - where F : FnMut(&Self::State) -> Step; + type Iter = StallIterator> + where F : FnMut(&Self::State) -> Step, + E : Error; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { StallIterator { base_iterator : self.base_options.prepare(step_fn), stall : self.stall, @@ -531,15 +622,16 @@ } } -impl AlgIterator +impl AlgIterator for StallIterator -where BaseIterator : AlgIterator, +where BaseIterator : AlgIterator, U : SignedNum + PartialOrd { type State = BaseIterator::State; type Output = BaseIterator::Output; + type Err = BaseIterator::Err; #[inline] - fn step(&mut self) -> Step { + fn step(&mut self) -> Step { match self.base_iterator.step() { Step::Result(nv) => { let previous_v = self.previous_value; @@ -584,13 +676,15 @@ for ValueIteratorFactory where BaseFactory : AlgIteratorFactory, U : SignedNum + PartialOrd { - type Iter = ValueIterator> - where F : FnMut(&Self::State) -> Step; + type Iter = ValueIterator> + where F : FnMut(&Self::State) -> Step, + E : Error; type State = BaseFactory::State; type Output = BaseFactory::Output; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { ValueIterator { base_iterator : self.base_options.prepare(step_fn), target : self.target @@ -602,15 +696,16 @@ } } -impl AlgIterator +impl AlgIterator for ValueIterator -where BaseIterator : AlgIterator, +where BaseIterator : AlgIterator, U : SignedNum + PartialOrd { type State = BaseIterator::State; type Output = BaseIterator::Output; + type Err = BaseIterator::Err; #[inline] - fn step(&mut self) -> Step { + fn step(&mut self) -> Step { match self.base_iterator.step() { Step::Result(v) => { if v <= self.target { @@ -673,12 +768,14 @@ where BaseFactory : AlgIteratorFactory, BaseFactory::Output : 'log { type State = BaseFactory::State; - type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter> - where F : FnMut(&Self::State) -> Step; + type Iter = LoggingIterator<'log, BaseFactory::Output, BaseFactory::Iter> + where F : FnMut(&Self::State) -> Step, + E : Error; type Output = (); - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { LoggingIterator { base_iterator : self.base_options.prepare(step_fn), logger : self.logger, @@ -691,15 +788,16 @@ } } -impl<'log, V, BaseIterator> AlgIterator +impl<'log, BaseIterator> AlgIterator for LoggingIterator<'log, BaseIterator::Output, BaseIterator> -where BaseIterator : AlgIterator, +where BaseIterator : AlgIterator, BaseIterator::Output : 'log { type State = BaseIterator::State; type Output = (); + type Err = BaseIterator::Err; #[inline] - fn step(&mut self) -> Step { + fn step(&mut self) -> Step { 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, G : Fn(usize, BaseFactory::Output) -> U { type State = BaseFactory::State; - type Iter = MappingIterator> - where F : FnMut(&Self::State) -> Step; + type Iter = MappingIterator> + where F : FnMut(&Self::State) -> Step, + E : Error; type Output = U; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { MappingIterator { base_iterator : self.base_options.prepare(step_fn), map : self.map @@ -758,19 +859,21 @@ } } -impl AlgIterator +impl AlgIterator for MappingIterator -where BaseIterator : AlgIterator, +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 { + fn step(&mut self) -> Step { 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 where BaseFactory : AlgIteratorFactory { type State = BaseFactory::State; - type Iter = TimingIterator> - where F : FnMut(&Self::State) -> Step; + type Iter = TimingIterator> + where F : FnMut(&Self::State) -> Step, + E : Error; type Output = Timed; - fn prepare(self, step_fn : F) -> Self::Iter - where F : FnMut(&Self::State) -> Step { + fn prepare(self, step_fn : F) -> Self::Iter + where F : FnMut(&Self::State) -> Step, + E : Error { TimingIterator { base_iterator : self.0.prepare(step_fn), start_time : ProcessTime::now() @@ -837,14 +942,15 @@ } } -impl AlgIterator +impl AlgIterator for TimingIterator -where BaseIterator : AlgIterator { +where BaseIterator : AlgIterator { type State = BaseIterator::State; type Output = Timed; + type Err = BaseIterator::Err; #[inline] - fn step(&mut self) -> Step { + fn step(&mut self) -> Step { 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), } }