use crate::traits::hkt::HKT;
use crate::traits::monoid::Monoid;
use std::ops::{Add, Mul};
pub trait Foldable: HKT {
fn fold_left<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&U, &Self::Source) -> U;
fn fold_right<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&Self::Source, &U) -> U;
#[inline]
fn fold_map<M: Monoid + Clone, F>(&self, f: F) -> M
where
F: Fn(&Self::Source) -> M,
{
self.fold_left(&M::empty(), |acc, x| acc.combine(&f(x)))
}
#[inline]
fn fold_monoid<M: Monoid + Clone>(&self) -> M
where
Self::Source: Clone + Into<M>,
{
self.fold_left(&M::empty(), |acc, x| acc.combine(&x.clone().into()))
}
#[inline]
fn length(&self) -> usize {
self.fold_left(&0, |acc, _| acc + 1)
}
#[inline]
fn is_empty(&self) -> bool {
self.length() == 0
}
}
pub trait FoldableExt: Foldable {
#[inline]
fn find<F>(&self, pred: F) -> Option<Self::Source>
where
F: Fn(&Self::Source) -> bool,
Self::Source: Clone,
{
self.fold_left(&None, |acc, x| {
if acc.is_some() {
acc.clone()
} else if pred(x) {
Some(x.clone())
} else {
None
}
})
}
#[inline]
fn all<F>(&self, pred: F) -> bool
where
F: Fn(&Self::Source) -> bool,
{
self.fold_left(&true, |acc, x| *acc && pred(x))
}
#[inline]
fn any<F>(&self, pred: F) -> bool
where
F: Fn(&Self::Source) -> bool,
{
self.fold_left(&false, |acc, x| *acc || pred(x))
}
#[inline]
fn contains(&self, value: &Self::Source) -> bool
where
Self::Source: PartialEq,
{
self.any(|x| x == value)
}
#[inline]
fn fold_option<B, F>(&self, f: F) -> Option<B>
where
F: Fn(&Self::Source) -> Option<B>,
B: Monoid + Clone,
{
self.fold_left(&Some(B::empty()), |acc, x| match (acc, f(x)) {
(Some(a), Some(b)) => Some(a.combine(&b)),
_ => None,
})
}
#[inline]
fn is_sorted(&self) -> bool
where
Self::Source: Clone + Ord,
{
self.fold_left(&(true, None), |(is_sorted, prev), curr| {
if !is_sorted {
(false, Some(curr.clone()))
} else {
match prev {
None => (true, Some(curr.clone())),
Some(p) => (p <= curr, Some(curr.clone())),
}
}
})
.0
}
#[inline]
fn to_vec(&self) -> Vec<Self::Source>
where
Self::Source: Clone,
{
self.fold_left(&Vec::new(), |acc, x| {
let mut new_acc = acc.clone();
new_acc.push(x.clone());
new_acc
})
}
#[inline]
fn sum_values(&self) -> Self::Source
where
Self::Source: Add<Output = Self::Source> + Default + Clone,
{
self.fold_left(&Self::Source::default(), |acc, x| acc.clone() + x.clone())
}
#[inline]
fn product_values(&self) -> Self::Source
where
Self::Source: Mul<Output = Self::Source> + From<u8> + Clone,
{
self.fold_left(&Self::Source::from(1), |acc, x| acc.clone() * x.clone())
}
#[inline]
fn maximum(&self) -> Option<Self::Source>
where
Self::Source: Ord + Clone,
{
self.fold_left(&None, |max: &Option<Self::Source>, x| match max {
None => Some(x.clone()),
Some(current_max) => Some(if x > current_max {
x.clone()
} else {
current_max.clone()
}),
})
}
#[inline]
fn minimum(&self) -> Option<Self::Source>
where
Self::Source: Ord + Clone,
{
self.fold_left(&None, |min: &Option<Self::Source>, x| match min {
None => Some(x.clone()),
Some(current_min) => Some(if x < current_min {
x.clone()
} else {
current_min.clone()
}),
})
}
#[inline]
fn reduce<F>(&self, f: F) -> Option<Self::Source>
where
F: Fn(&Self::Source, &Self::Source) -> Self::Source,
Self::Source: Clone,
{
self.fold_left(&None, |acc: &Option<Self::Source>, x| match acc {
None => Some(x.clone()),
Some(a) => Some(f(a, x)),
})
}
}
impl<T: Foldable> FoldableExt for T {}
impl<A: Clone> Foldable for Vec<A> {
#[inline]
fn fold_left<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&U, &A) -> U,
{
let mut acc = init.clone();
for item in self {
acc = f(&acc, item);
}
acc
}
#[inline]
fn fold_right<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&A, &U) -> U,
{
let mut acc = init.clone();
for item in self.iter().rev() {
acc = f(item, &acc);
}
acc
}
}
impl<A: Clone> Foldable for Option<A> {
#[inline]
fn fold_left<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&U, &A) -> U,
{
match self {
Some(a) => f(init, a),
None => init.clone(),
}
}
#[inline]
fn fold_right<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&A, &U) -> U,
{
match self {
Some(a) => f(a, init),
None => init.clone(),
}
}
}
impl<A: Clone, E: Clone> Foldable for Result<A, E> {
#[inline]
fn fold_left<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&U, &A) -> U,
{
match self {
Ok(a) => f(init, a),
Err(_) => init.clone(),
}
}
#[inline]
fn fold_right<U: Clone, F>(&self, init: &U, f: F) -> U
where
F: Fn(&A, &U) -> U,
{
match self {
Ok(a) => f(a, init),
Err(_) => init.clone(),
}
}
}