Skip to content

Commit

Permalink
WIP Foldable concept
Browse files Browse the repository at this point in the history
  • Loading branch information
jost125 committed Jan 21, 2022
1 parent ecd43eb commit a22100f
Showing 1 changed file with 169 additions and 0 deletions.
169 changes: 169 additions & 0 deletions src/Bonami/Collection/Foldable1.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
<?php

declare(strict_types=1);

namespace Bonami\Collection;

use Bonami\Collection\Monoid\Monoid;

/** @template T */
trait Foldable1
{
/**
* Reduce to single value by applying $reducer on each item with $carry from each step.
*
* Complexity: o(n)
*
* @template R
*
* @param callable(R, T, int): R $reducer - ($carry: mixed, $item: mixed, $key: int) => mixed
* @param R $initialReduction - initial value used as seed for $carry
*
* @return R - reduced values. If the list is empty, $initialReduction is directly returned
*/
abstract public function reduce(callable $reducer, $initialReduction);

/**
* Reduce (folds) to single value using Monoid
*
* Complexity: o(n)
*
* @see sum - for trivial summing
*
* @param Monoid<T> $monoid
*
* @return T
*/
public function mfold(Monoid $monoid)
{
return $this->reduce(static function ($carry, $next) use ($monoid) {
return $monoid->concat($carry, $next);
}, $monoid->getEmpty());
}

/**
* Converts items to numbers and then sums them up.
*
* Complexity: o(n)
*
* @see mfold - for folding diferent types of items (E.g. classes representing BigNumbers and so on)
*
* @param callable(T): (int|float) $itemToNumber
*
* @return int|float
*/
public function sum(callable $itemToNumber)
{
return $this->reduce(static function ($carry, $next) use ($itemToNumber) {
return $carry + $itemToNumber($next);
}, 0);
}

/**
* Finds first item by given predicate where it matches
*
* Complexity: o(n)
*
* @see exists - if you just need to check if something matches by predicate
* @see findKey - if you need to get key by predicate
*
* @param callable(T, int): bool $predicate
*
* @return Option<T>
*/
public function find(callable $predicate): Option
{
return $this->reduce(
static fn ($carry, $next, int $i) => $carry->isDefined()
? $carry
: ($predicate($next, $i) === true ? Option::some($next) : Option::none()),
Option::none()
);
}

/**
* Gets the very first value
*
* Complexity: o(1)
*
* @return Option<T> item wrapped with Option::some or Option::none if empty
*/
public function head(): Option
{
return $this->find(static fn () => true);
}

/**
* Finds minimal value defined by comparator
*
* Complexity: o(n)
*
* @param null|callable(T, T): int $comparator - classic comparator returning 1, 0 or -1
* if no comparator is passed, $first <=> $second is used
*
* @return Option<T> minimal value wrapped in Option::some or Option::none when list is empty
*/
public function min(?callable $comparator = null): Option
{
$comparator ??= comparator();
$min = Option::lift2(static fn ($a, $b) => $comparator($a, $b) <= 0 ? $a : $b);
return $this->reduce(
static fn ($next, $carry) => $min($next, Option::of($carry)),
$this->head()
);
}

/**
* Finds maximal value defined by comparator
*
* Complexity: o(n)
*
* @param null|callable(T, T): int $comparator - classic comparator returning 1, 0 or -1
* if no comparator is passed, $first <=> $second is used
*
* @return Option<T> minimal value wrapped in Option::some or Option::none when list is empty
*/
public function max(?callable $comparator = null): Option
{
$comparator ??= comparator();
$max = Option::lift2(static fn ($a, $b) => $comparator($a, $b) > 0 ? $a : $b);
return $this->reduce(
static fn ($next, $carry) => $max($next, Option::of($carry)),
$this->head()
);
}

/**
* Checks if AT LEAST ONE item satisfies predicate.
*
* Complexity: o(n)
*
* @see find - if you need to get item by predicate
* @see all - if you need to check if ALL items in List satisfy predicate
*
* @param callable(T, int): bool $predicate
*
* @return bool
*/
public function exists(callable $predicate): bool
{
return $this->find($predicate)->isDefined();
}

/**
* Checks if ALL items satisfy predicate
*
* Complexity: o(n)
*
* @see exists - if you need to check if AT LEAST ONE item in List satisfy predicate
* @see find - if you need to get item by predicate
*
* @param callable(T, int): bool $predicate
*
* @return bool
*/
public function all(callable $predicate): bool
{
return !$this->exists(static fn ($i) => !$predicate($i));
}
}

0 comments on commit a22100f

Please sign in to comment.