use std::ops::{Add, Mul};
use super::Monoid;
pub trait Foldable<A> {
fn foldr<F, B>(self, folder: F, init: B) -> B
where
F: Fn(B, A) -> B;
fn fold_map<F, M>(self, f: F) -> M
where
Self: Sized,
F: Fn(A) -> M,
M: Monoid,
{
self.foldr(|m_res, a| m_res.mappend(f(a)), M::mempty())
}
fn fold1<F>(self, folder: F) -> Option<A>
where
Self: Sized,
F: Fn(A, A) -> A,
{
self.foldr(
|a_res, a| {
if let Some(r) = a_res {
Some(folder(a, r))
} else {
Some(a)
}
},
None,
)
}
fn null(self) -> bool
where
Self: Sized,
{
self.foldr(|_, _| false, true)
}
fn to_list(self) -> Vec<A>
where
Self: Sized,
{
self.foldr(
|mut v_res, elem| {
v_res.push(elem);
v_res
},
Vec::new(),
)
}
fn length(self) -> usize
where
Self: Sized,
{
self.foldr(|len, _| len + 1, 0)
}
}
pub trait FoldableExtMonoid<M>: Foldable<M> + Sized
where
M: Monoid,
{
fn fold(self) -> M {
self.foldr(|m_res, m| m_res.mappend(m), M::mempty())
}
}
pub trait FoldableExtEq<A>: Foldable<A> + Sized
where
A: PartialEq,
{
fn elem(self, search_for: A) -> bool {
self.foldr(|b_res, elem| b_res || search_for.eq(&elem), false)
}
}
pub trait FoldableExtOrd<A>: Foldable<A> + Sized
where
A: PartialOrd,
{
fn maximum(self) -> Option<A> {
self.foldr(
|prev_max, elem| {
if let Some(max) = prev_max {
Some(if elem > max { elem } else { max })
} else {
Some(elem)
}
},
None,
)
}
fn minimum(self) -> Option<A> {
self.foldr(
|prev_min, elem| {
if let Some(min) = prev_min {
Some(if elem < min { elem } else { min })
} else {
Some(elem)
}
},
None,
)
}
}
pub trait FoldableExtSum<A>: Foldable<A> + Sized
where
A: Add<Output = A> + Monoid,
{
fn sum(self) -> A {
self.foldr(|sum, a| sum.add(a), A::mempty())
}
}
pub trait FoldableExtProd<A>: Foldable<A> + Sized
where
A: Mul<Output = A> + Monoid,
{
fn sum(self) -> A {
self.foldr(|sum, a| sum.mul(a), A::mempty())
}
}