rosavox/lib/alveolata/list/functions.php
2025-05-23 07:33:29 +00:00

317 lines
5.7 KiB
PHP

<?php
namespace alveolata\list_;
// require_once(DIR_ALVEOLATA . '/definitions.php');
/**
* returns a list of integers from 0 to length-1
*
* @param int $length {integer}
* @return array {list<integer>}
* @author Christian Fraß <frass@greenscale.de>
*/
function sequence(
int $length
) : array
{
return (
($length <= 0)
? []
: array_merge(sequence($length-1), [$length-1])
);
}
/**
* @param array $list1 {list<§x>}
* @param array $list2 {list<§x>}
* @return array {list<§x>}
* @author Christian Fraß <frass@greenscale.de>
*/
function concat(
array $list1,
array $list2
) : array
{
return array_merge($list1, $list2);
}
/**
* @param array $list {list<§x>}
* @param \Closure $predicate {function<§x,boolean>}
* @return array {list<§x>}
* @author Christian Fraß <frass@greenscale.de>
*/
function filter(
array $list,
\Closure $predicate
) : array
{
$list_ = [];
foreach ($list as $value) {
if ($predicate($value)) {
array_push($list_, $value);
}
}
return $list_;
}
/**
* @param array $list {list<§x>}
* @param \Closure $transformator {function<§x,§y>}
* @return array {list<§y>}
* @author Christian Fraß <frass@greenscale.de>
*/
function map(
array $list,
\Closure $transformator
) : array
{
$list_ = [];
foreach ($list as $value) {
$value_ = $transformator($value);
array_push($list_, $value_);
}
return $list_;
}
/**
* @param array $list {list<§x>}
* @param mixed $start {§y}
* @param \Closure $aggregator {function<§y,§x,§y>}
* @return mixed {§y}
* @author Christian Fraß <frass@greenscale.de>
*/
function reduce_left(
array $list,
$start,
\Closure $aggregator
)
{
$result = $start;
foreach ($list as $current) {
$result = $aggregator($result, $current);
}
return $result;
}
/**
* @param array $list {list<§x>}
* @param mixed $start {§y}
* @param \Closure $aggregator {function<§x,§y,§y>}
* @return mixed {§y}
* @author Christian Fraß <frass@greenscale.de>
*/
function reduce_right(
array $list,
$start,
\Closure $aggregator
)
{
$result = $start;
foreach (array_reverse($list) as $current) {
$result = $aggregator($current, $result);
}
return $result;
}
/**
* @param array $list {list<§x>}
* @param mixed $start {§y}
* @param \Closure $aggregator {function<§y,§x,§y>}
* @return mixed {§y}
* @author Christian Fraß <frass@greenscale.de>
*/
function reduce(
array $list,
$start,
\Closure $aggregator
)
{
return reduce_left($list, $start, $aggregator);
}
/**
* @param array $list {list<§x>}
* @param \Closure $predicate {function<§x,bool>}
* @return bool {boolean}
* @author Christian Fraß <frass@greenscale.de>
*/
function some(
array $list,
\Closure $predicate
) : bool
{
foreach ($list as $current) {
if ($predicate($current)) {
return true;
}
}
return false;
}
/**
* @param array $list {list<§x>}
* @param \Closure $predicate {function<§x,bool>}
* @return bool {boolean}
* @author Christian Fraß <frass@greenscale.de>
*/
function every(
array $list,
\Closure $predicate
) : bool
{
foreach ($list as $current) {
if (! $predicate($current)) {
return false;
}
}
return true;
}
/**
* @param array $list1 {list<§x>}
* @param array $list2 {list<§y>}
* @param bool $cut {boolean} whether to take the least length in case the lists have different lengths
* @return array {list<record<first:§x,second:§y>>}
* @throw \Exception if lists have different lengths and $cut = false
* @author Christian Fraß <frass@greenscale.de>
*/
function zip(
array $list1,
array $list2,
bool $cut = true
) : array
{
$l1 = count($list1);
$l2 = count($list2);
if (! ($l1 === $l2)) {
if (! $cut) {
throw (new \Exception('lists have different lengths'));
}
else {
$length = $l1;
}
}
else {
$length = min($l1, $l2);
}
$list3 = [];
for ($index = 0; $index < $length; $index += 1) {
$pair = [
'first' => $list1[$index],
'second' => $list2[$index],
];
array_push($list3, $pair);
}
return $list3;
}
/**
* @param array $list {list<§x>}
* @param \Closure $order {function<§x,§x,boolean>}
* @return array {list<§x>}
* @throws \Exception if the sorting fails
* @author Christian Fraß <frass@greenscale.de>
*/
function sort(
array $list,
\Closure $order = null
) : array
{
if (is_null($order)) {
$order = (function ($x, $y) {return ($x <= $y);});
}
$copy = array_map(function ($x) {return $x;}, $list);
$successful = usort(
$copy,
function ($x, $y) use ($order) : int {
return ($order($x, $y) ? (-1) : (+1));
}
);
if (! $successful) {
throw (new \Exception('alveolata_list_sort_failed'));
}
else {
return $copy;
}
}
/**
* @param array $list {list<§x>}
* @param \Closure $collation {function<§x,§x,boolean>}
* @return array {list<list<§x>>}
*/
function group(
array $list,
\Closure $collation
) : array
{
$groups = [];
foreach ($list as $element) {
$found = false;
foreach ($groups as &$group) {
if ($collation($group[0], $element)) {
array_push($group, $element);
$found = true;
break;
}
}
unset($group);
if (! $found) {
$group = [$element];
array_push($groups, $group);
}
}
return $groups;
}
/**
* @param array $x {list<type_x>}
* @param array $y {list<type_y>}
* @param ?array $options {
* (
* null
* |
* record<
* ?combinator:function<tuple<type_x,type_y>,any>,
* >
* )
* }
* @return array {list<tuple<type_x,type_y>>}
*/
function product(
array $x,
array $y,
?array $options = null
) : array
{
$options = \array_merge(
[
'combinator' => (fn($u, $v) => [$u, $v])
],
($options ?? [])
);
$z = [];
foreach ($x as $u) {
foreach ($y as $v) {
\array_push($z, $options['combinator']($u, $v));
}
}
return $z;
}
?>