Skip to content

Commit

Permalink
.
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Ockajak committed Jul 10, 2024
1 parent 1fcbfb8 commit 2844693
Show file tree
Hide file tree
Showing 15 changed files with 451 additions and 70 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ use cantrip::*;

let data = vec![1, 2, 3];

data.fold(0, |r, x| r + x); // 6
data.fold_to(0, |r, x| r + x); // 6

data.filter(|&x| x > 1); // vec![2, 3]

Expand Down Expand Up @@ -81,7 +81,8 @@ data.group_by(|x| x % 2); // HashMap::from([(0, vec![2]), (1, vec![1, 3]
| *flat_map* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *flat_map_to* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *flat* | :heavy_check_mark: | | :heavy_check_mark: | |
| *fold* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *fold* | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| *fold_to* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *frequencies* | :heavy_check_mark: | | | |
| *frequencies_by* | :heavy_check_mark: | | | |
| *group_by* | :heavy_check_mark: | | :heavy_check_mark: | |
Expand Down Expand Up @@ -129,7 +130,8 @@ data.group_by(|x| x % 2); // HashMap::from([(0, vec![2]), (1, vec![1, 3]
| *product* | :heavy_check_mark: | | :heavy_check_mark: | |
| *product_keys* | | | | :heavy_check_mark: |
| *product_values* | | | | :heavy_check_mark: |
| *reduce* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *reduce* | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
| *reduce_to* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *replace* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *replace_all* | :heavy_check_mark: | | :heavy_check_mark: | :heavy_check_mark: |
| *replace_at* | :heavy_check_mark: | | | |
Expand Down
37 changes: 22 additions & 15 deletions src/extensions/collectible.rs
Original file line number Diff line number Diff line change
Expand Up @@ -561,28 +561,32 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
/// Folds every element into an accumulator by applying an operation,
/// returning the final result.
///
/// `fold()` takes two arguments: an initial value, and a closure with two
/// `fold_to()` takes two arguments: an initial value, and a closure with two
/// arguments: an 'accumulator', and an element. The closure returns the value that
/// the accumulator should have for the next iteration.
///
/// The initial value is the value the accumulator will have on the first
/// call.
///
/// After applying this closure to every element of the collection, `fold()`
/// After applying this closure to every element of the collection, `fold_to()`
/// returns the accumulator.
///
/// This operation is sometimes called 'reduce' or 'inject'.
///
/// Folding is useful whenever you have a collection of something, and want
/// to produce a single value from it.
///
/// This is a consuming variant of [`fold`].
///
/// Note: [`reduce()`] can be used to use the first element as the initial
/// value, if the accumulator type and item type is the same.
///
/// Note: `fold()` combines elements in a *left-associative* fashion. For associative
/// Note: `fold_to()` combines elements in a *left-associative* fashion. For associative
/// operators like `+`, the order the elements are combined in is not important, but for non-associative
/// operators like `-` the order will affect the final result.
/// For a *right-associative* version of `fold()`, see [`rfold()`].
/// For a *right-associative* version of `fold_to()`, see [`rfold_to()`].
///
/// [`fold`]: crate::Traversable::fold
///
/// # Examples
///
Expand All @@ -594,7 +598,7 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
/// let a = vec![1, 2, 3];
///
/// // the sum of all the elements of the array
/// let sum = a.fold(0, |acc, x| acc + x);
/// let sum = a.fold_to(0, |acc, x| acc + x);
///
/// assert_eq!(sum, 6);
/// ```
Expand All @@ -610,7 +614,7 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
///
/// And so, our final result, `6`.
///
/// This example demonstrates the left-associative nature of `fold()`:
/// This example demonstrates the left-associative nature of `fold_to()`:
/// it builds a string, starting with an initial value
/// and continuing with each element from the front until the back:
///
Expand All @@ -621,15 +625,15 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
///
/// let zero = "0".to_string();
///
/// let result = numbers.fold(zero, |acc, x| {
/// let result = numbers.fold_to(zero, |acc, x| {
/// format!("({acc} + {x})")
/// });
///
/// assert_eq!(result, "(((((0 + 1) + 2) + 3) + 4) + 5)");
/// ```
/// It's common for people who haven't used collections a lot to
/// use a `for` loop with a list of things to build up a result. Those
/// can be turned into `fold()`s:
/// can be turned into `fold_to()`s:
///
/// [`for`]: ../../book/ch03-05-control-flow.html#looping-through-a-collection-with-for
///
Expand All @@ -646,13 +650,13 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
/// }
///
/// // fold:
/// let result2 = numbers.fold(0, |acc, x| acc + x);
/// let result2 = numbers.fold_to(0, |acc, x| acc + x);
///
/// // they're the same
/// assert_eq!(result, result2);
/// ```
#[inline]
fn fold<B>(self, initial_value: B, function: impl FnMut(B, Item) -> B) -> B
fn fold_to<B>(self, initial_value: B, function: impl FnMut(B, Item) -> B) -> B
where
Self: Sized,
{
Expand Down Expand Up @@ -1084,11 +1088,14 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
/// result of the reduction.
///
/// The reducing function is a closure with two arguments: an 'accumulator', and an element.
/// For collections with at least one element, this is the same as [`fold()`]
/// For collections with at least one element, this is the same as [`fold_to()`]
/// with the first element of the collection as the initial accumulator value, folding
/// every subsequent element into it.
///
/// [`fold()`]: crate::Traversable::fold
/// This is a consuming variant of [`reduce`].
///
/// [`fold_to()`]: Collectible::fold_to
/// [`reduce()`]: crate::Traversable::reduce
///
/// # Example
///
Expand All @@ -1098,18 +1105,18 @@ pub trait Collectible<Item>: IntoIterator<Item = Item> {
/// # let source = vec![1, 2, 3];
/// let a = vec![1, 2, 3];
///
/// let reduced = a.reduce(|acc, e| acc + e).unwrap();
/// let reduced = a.reduce_to(|acc, e| acc + e).unwrap();
///
/// assert_eq!(reduced, 6);
///
/// // Which is equivalent to doing it with `fold`:
/// # let a = source.clone();
/// let folded = a.fold(0, |acc, e| acc + e);
/// let folded = a.fold_to(0, |acc, e| acc + e);
///
/// assert_eq!(reduced, folded);
/// ```
#[inline]
fn reduce(self, function: impl FnMut(Item, Item) -> Item) -> Option<Item>
fn reduce_to(self, function: impl FnMut(Item, Item) -> Item) -> Option<Item>
where
Self: Sized,
{
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/binary_heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ impl<Item> Traversable<Item> for BinaryHeap<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -59,6 +64,11 @@ impl<Item> Traversable<Item> for BinaryHeap<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/btree_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ impl<Key: Ord, Value> Map<Key, Value> for BTreeMap<Key, Value> {
flat_map_pairs(self.iter(), |&x| function(x))
}

#[inline]
fn fold<B>(self, initial_value: B, function: impl FnMut(B, (&Key, &Value)) -> B) -> B {
fold_pairs(self.iter(), initial_value, function)
}

#[inline]
fn map<L, W>(&self, mut function: impl FnMut((&Key, &Value)) -> (L, W)) -> Self::This<L, W>
where
Expand Down Expand Up @@ -116,6 +121,11 @@ impl<Key: Ord, Value> Map<Key, Value> for BTreeMap<Key, Value> {
partition_map_pairs(self.iter(), function)
}

#[inline]
fn reduce(self, function: impl FnMut((&Key, &Value), (&Key, &Value)) -> (Key, Value)) -> Option<(Key, Value)> {
reduce_pairs(self.iterator(), function)
}

#[inline]
fn scan<S, L, W>(
self, initial_state: S, function: impl FnMut(&mut S, (&Key, &Value)) -> Option<(L, W)>,
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/btree_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ impl<Item> Traversable<Item> for BTreeSet<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -59,6 +64,11 @@ impl<Item> Traversable<Item> for BTreeSet<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/hash_map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,11 @@ impl<Key, Value> Map<Key, Value> for HashMap<Key, Value> {
flat_map_pairs(self.iter(), |&x| function(x))
}

#[inline]
fn fold<B>(self, initial_value: B, function: impl FnMut(B, (&Key, &Value)) -> B) -> B {
fold_pairs(self.iter(), initial_value, function)
}

#[inline]
fn map<L, W>(&self, mut function: impl FnMut((&Key, &Value)) -> (L, W)) -> Self::This<L, W>
where
Expand Down Expand Up @@ -115,6 +120,11 @@ impl<Key, Value> Map<Key, Value> for HashMap<Key, Value> {
partition_map_pairs(self.iter(), function)
}

#[inline]
fn reduce(self, function: impl FnMut((&Key, &Value), (&Key, &Value)) -> (Key, Value)) -> Option<(Key, Value)> {
reduce_pairs(self.iterator(), function)
}

#[inline]
fn scan<S, L, W>(
self, initial_state: S, function: impl FnMut(&mut S, (&Key, &Value)) -> Option<(L, W)>,
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/hash_set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ impl<Item> Traversable<Item> for HashSet<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -60,6 +65,11 @@ impl<Item> Traversable<Item> for HashSet<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/linked_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ impl<Item> Traversable<Item> for LinkedList<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -61,6 +66,11 @@ impl<Item> Traversable<Item> for LinkedList<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ impl<Item> Traversable<Item> for [Item] {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -61,6 +66,11 @@ impl<Item> Traversable<Item> for [Item] {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ impl<Item> Traversable<Item> for Vec<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -60,6 +65,11 @@ impl<Item> Traversable<Item> for Vec<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
10 changes: 10 additions & 0 deletions src/extensions/collections/vec_deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ impl<Item> Traversable<Item> for VecDeque<Item> {
self.iter().find_map(function)
}

#[inline]
fn fold<B>(&self, initial_value: B, function: impl FnMut(B, &Item) -> B) -> B {
fold(self.iter(), initial_value, function)
}

#[inline]
fn max_by(&self, mut compare: impl FnMut(&Item, &Item) -> Ordering) -> Option<&Item> {
self.iter().max_by(|&x, &y| compare(x, y))
Expand Down Expand Up @@ -61,6 +66,11 @@ impl<Item> Traversable<Item> for VecDeque<Item> {
minmax_by_key(self.iter(), to_key)
}

#[inline]
fn reduce(&self, function: impl FnMut(&Item, &Item) -> Item) -> Option<Item> {
reduce(self.iter(), function)
}

#[inline]
fn subset<'a>(&'a self, elements: &'a impl Iterable<Item<'a> = &'a Item>) -> bool
where
Expand Down
Loading

0 comments on commit 2844693

Please sign in to comment.