Skip to content

Commit

Permalink
Refactor reducer callbacks.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Sep 2, 2020
1 parent f74152b commit 8f83368
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 96 deletions.
5 changes: 5 additions & 0 deletions spec/loophp/collection/CollectionSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -970,6 +970,11 @@ public function it_can_group()
19,
],
]);

$input = range(0, 20);
$this::fromIterable($input)
->group(static function () {return null; })
->shouldIterateAs($input);
}

public function it_can_has(): void
Expand Down
2 changes: 1 addition & 1 deletion src/Contract/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -140,11 +140,11 @@
* @template-extends Pluckable<TKey, T>
* @template-extends Prependable<TKey, T>
* @template-extends Productable<TKey, T>
* @template-extends RSampleable<TKey, T>
* @template-extends Randomable<TKey, T>
* @template-extends Reduceable<TKey, T>
* @template-extends Reductionable<TKey, T>
* @template-extends Reverseable<TKey, T>
* @template-extends RSampleable<TKey, T>
* @template-extends Scaleable<TKey, T>
* @template-extends Shuffleable<TKey, T>
* @template-extends Sinceable<TKey, T>
Expand Down
16 changes: 6 additions & 10 deletions src/Operation/Compact.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,14 @@ static function (...$values): Closure {
* @psalm-return Generator<TKey, T>
*/
static function (Iterator $iterator) use ($values): Generator {
$values = [] === $values ? [null] : $values;
$filterCallback = static function (array $values): Closure {
return static function ($value) use ($values): bool {
return !in_array($value, $values, true);
};
};

/** @psalm-var callable(Iterator<TKey, T>):Generator<TKey, T> $filter */
$filter = Filter::of()(
/**
* @param mixed $value
* @psalm-param T $value
*/
static function ($value) use ($values): bool {
return !in_array($value, $values, true);
}
);
$filter = Filter::of()($filterCallback([] === $values ? [null] : $values));

return yield from $filter($iterator);
};
Expand Down
50 changes: 36 additions & 14 deletions src/Operation/Map.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,22 +32,44 @@ static function (callable ...$callbacks): Closure {
* @psalm-return Generator<TKey, T>
*/
static function (Iterator $iterator) use ($callbacks): Generator {
foreach ($iterator as $key => $value) {
$callback =
/**
* @param mixed $carry
* @psalm-param T $carry
*
* @psalm-param callable(T, TKey): T $callback
*
* @psalm-return T
*/
static function ($carry, callable $callback) use ($value, $key) {
return $callback($value, $key);
};
$callbackFactory =
/**
* @psalm-param TKey $key
*
* @psalm-return Closure(T): Closure(T, callable(T, TKey): T): T
*
* @param mixed $key
*/
static function ($key): Closure {
return
/**
* @psalm-param T $value
*
* @psalm-return Closure(T, callable(T, TKey): T): T
*
* @param mixed $value
*/
static function ($value) use ($key): Closure {
return
/**
* @psalm-param T $carry
* @psalm-param callable(T, TKey): T $callback
*
* @psalm-return T
*
* @param mixed $carry
*/
static function ($carry, callable $callback) use ($key, $value) {
return $callback($value, $key);
};
};
};

yield $key => array_reduce($callbacks, $callback, $value);
// phpcs:disable
foreach ($iterator as $key => $value) {
yield $key => array_reduce($callbacks, $callbackFactory($key)($value));
}
// phpcs:enable
};
};
}
Expand Down
29 changes: 22 additions & 7 deletions src/Operation/RSample.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,31 @@
*/
final class RSample extends AbstractOperation
{
/**
* @return Closure(float): Closure(Iterator<TKey, T>): Generator<TKey, T>
*/
public function __invoke(): Closure
{
return static function (float $probability): Closure {
return static function (Iterator $iterator) use ($probability): Generator {
$callback = static function () use ($probability): bool {
return (mt_rand() / mt_getrandmax()) < $probability;
};
return
/**
* @psalm-return Closure(Iterator<TKey, T>): Generator<TKey, T>
*/
static function (float $probability): Closure {
return
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Generator<TKey, T>
*/
static function (Iterator $iterator) use ($probability): Generator {
$callback = static function (float $probability): Closure {
return static function () use ($probability): bool {
return (mt_rand() / mt_getrandmax()) < $probability;
};
};

return yield from Filter::of()($callback)($iterator);
return yield from Filter::of()($callback($probability))($iterator);
};
};
};
}
}
70 changes: 48 additions & 22 deletions src/Operation/Since.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,32 +15,58 @@
*/
final class Since extends AbstractOperation
{
/**
* @psalm-return Closure((callable(T, TKey):bool)...): Closure(Iterator<TKey, T>): Generator<TKey, T>
*/
public function __invoke(): Closure
{
return static function (callable ...$callbacks): Closure {
return static function (Iterator $iterator) use ($callbacks): Generator {
while ($iterator->valid()) {
$result = array_reduce(
$callbacks,
static function (bool $carry, callable $callable) use ($iterator): bool {
return ($callable($iterator->current(), $iterator->key())) ?
$carry :
false;
},
true
);

if (false !== $result) {
break;
}
return
/**
* @psalm-param callable(T, TKey):bool ...$callbacks
*
* @psalm-return Closure(Iterator<TKey, T>): Generator<TKey, T>
*/
static function (callable ...$callbacks): Closure {
return
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Generator<TKey, T>
*/
static function (Iterator $iterator) use ($callbacks): Generator {
$reducer =
/**
* @psalm-param Iterator<TKey, T> $iterator
*
* @psalm-return Closure(bool, callable(T, TKey): bool): bool
*/
static function (Iterator $iterator): Closure {
return
/**
* @psalm-param bool $carry
* @psalm-param callable(T, TKey): bool $callback
*/
static function (bool $carry, callable $callback) use ($iterator): bool {
return ($callback($iterator->current(), $iterator->key())) ?
$carry :
false;
};
};

while ($iterator->valid()) {
$result = array_reduce($callbacks, $reducer($iterator), true);

$iterator->next();
}
if (false !== $result) {
break;
}

for (; $iterator->valid(); $iterator->next()) {
yield $iterator->key() => $iterator->current();
}
$iterator->next();
}

for (; $iterator->valid(); $iterator->next()) {
yield $iterator->key() => $iterator->current();
}
};
};
};
}
}
27 changes: 18 additions & 9 deletions src/Operation/Sort.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,28 @@ static function (Iterator $iterator) use ($type, $callback): Generator {
'after' => [Unpack::of(), Flip::of()],
];

$sortCallback =
/**
* @psalm-param callable(T|TKey, T|TKey): int $callback
*
* @psalm-return Closure(array{0:TKey|T, 1:T|TKey}, array{0:TKey|T, 1:T|TKey}): int
*/
static function (callable $callback): Closure {
return
/**
* @psalm-param array{0:TKey|T, 1:T|TKey} $left
* @psalm-param array{0:TKey|T, 1:T|TKey} $right
*/
static function (array $left, array $right) use ($callback): int {
return $callback($left[1], $right[1]);
};
};

/** @psalm-var callable(Iterator<TKey, T>): Generator<int, array{0:TKey, 1:T}> | callable(Iterator<TKey, T>): Generator<int, array{0:T, 1:TKey}> $before */
$before = Compose::of()(...$operations['before']);

$arrayIterator = new ArrayIterator(iterator_to_array($before($iterator)));
$arrayIterator->uasort(
/**
* @psalm-param array{0:TKey|T, 1:T|TKey} $left
* @psalm-param array{0:TKey|T, 1:T|TKey} $right
*/
static function (array $left, array $right) use ($callback): int {
return $callback($left[1], $right[1]);
}
);
$arrayIterator->uasort($sortCallback($callback));

return yield from Compose::of()(...$operations['after'])($arrayIterator);
};
Expand Down
51 changes: 41 additions & 10 deletions src/Operation/Split.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,19 @@
*/
final class Split extends AbstractOperation
{
/**
* @return Closure((callable(T, TKey): bool)...): Closure(Iterator<TKey, T>): Generator<int, list<T>>
*/
public function __invoke(): Closure
{
return static function (callable ...$callbacks): Closure {
return
return
/**
* @psalm-param callable(T, TKey): bool ...$callbacks
*
* @psalm-return Closure(Iterator<TKey, T>): Generator<int, list<T>>
*/
static function (callable ...$callbacks): Closure {
return
/**
* @psalm-param Iterator<TKey, T> $iterator
* @psalm-param list<callable(T, TKey):(bool)> $callbacks
Expand All @@ -28,14 +37,36 @@ public function __invoke(): Closure
static function (Iterator $iterator) use ($callbacks): Generator {
$carry = [];

$reducer =
/**
* @psalm-param TKey $key
*
* @psalm-return Closure(T): Closure(bool, callable(T, TKey): bool): bool
*
* @param mixed $key
*/
static function ($key): Closure {
return
/**
* @psalm-param T $value
*
* @psalm-return Closure(bool, callable(T, TKey): bool): bool
*
* @param mixed $value
*/
static function ($value) use ($key): Closure {
return
/**
* @psalm-param callable(T, TKey): bool $callback
*/
static function (bool $carry, callable $callback) use ($key, $value): bool {
return $callback($value, $key) !== $carry;
};
};
};

foreach ($iterator as $key => $value) {
$callbackReturn = array_reduce(
$callbacks,
static function (bool $carry, callable $callback) use ($key, $value): bool {
return $callback($value, $key) !== $carry;
},
false
);
$callbackReturn = array_reduce($callbacks, $reducer($key)($value), false);

if (true === $callbackReturn && [] !== $carry) {
yield $carry;
Expand All @@ -50,6 +81,6 @@ static function (bool $carry, callable $callback) use ($key, $value): bool {
yield $carry;
}
};
};
};
}
}
Loading

0 comments on commit 8f83368

Please sign in to comment.