add memoized function helper

This commit is contained in:
Kacper Donat 2018-08-27 18:57:36 +02:00
parent e46fbb3cc0
commit 024d97d46c
6 changed files with 116 additions and 1 deletions

8
bin/psysh Normal file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env php
<?php
require_once __DIR__.'/../vendor/autoload.php';
use Kadet\Functional as f;
use Kadet\Functional\Predicates as p;
eval(\Psy\sh());

View File

@ -1,5 +1,5 @@
{
"name": "kadet/predicate",
"name": "kadet/functional",
"description": "Functional library for PHP",
"type": "library",
"license": "MIT",

View File

@ -0,0 +1,40 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
namespace Kadet\Functional\Utils;
use Kadet\Functional\Decorator;
class MemoizedFunction implements Decorator
{
private $decorated;
private $memoized = [];
public function __construct(callable $decorated)
{
$this->decorated = $decorated;
}
public function getDecorated(): callable
{
return $this->decorated;
}
public function __invoke(...$args)
{
$key = hash('sha256', print_r($args, true));
if (!array_key_exists($key, $this->memoized)) {
$this->memoized[$key] = ($this->decorated)(...$args);
}
return $this->memoized[$key];
}
}

View File

@ -12,6 +12,7 @@ namespace Kadet\Functional;
use Kadet\Functional\Utils\CurriedFunction;
use Kadet\Functional\Utils\FixedFunction;
use Kadet\Functional\Utils\FunctionPipeline;
use Kadet\Functional\Utils\MemoizedFunction;
use Kadet\Functional\Utils\PartiallyAppliedFunction;
function partial(callable $callable, ...$args): PartiallyAppliedFunction
@ -66,3 +67,8 @@ function fix(callable $callable, ...$args)
$f = new FixedFunction($callable);
return $args ? apply($f, ...$args) : $f;
}
function memoize(callable $callable)
{
return new MemoizedFunction($callable);
}

View File

@ -2,10 +2,12 @@
namespace Kadet\Functional;
use function foo\func;
use Kadet\Functional\Predicate\AllOfPredicate;
use Kadet\Functional\Predicate\AnyOfPredicate;
use Kadet\Functional\Predicate\ClosurePredicate;
use Kadet\Functional\Predicate\ConstantPredicate;
use Kadet\Functional\Reflection\ReflectionDecoratedFunction;
use Kadet\Functional\Reflection\ReflectionFixedFunction;
use Kadet\Functional\Reflection\ReflectionPartiallyAppliedFunction;
use Kadet\Functional\Utils\FixedFunction;
@ -70,6 +72,13 @@ function symbol($name = null)
return sprintf("%s(%s)", $name ?: 'symbol', $id);
}
function repeat($times, callable $callback)
{
for ($i = 0; $i < $times; $i++) {
$callback($times);
}
}
/**
* @param callable $callable
*
@ -83,6 +92,8 @@ function reflect(callable $callable): \ReflectionFunction
return new ReflectionPartiallyAppliedFunction($callable);
case $callable instanceof FixedFunction:
return new ReflectionFixedFunction($callable);
case $callable instanceof Decorator:
return new ReflectionDecoratedFunction($callable->getDecorated());
default:
return new \ReflectionFunction($callable);
}

View File

@ -0,0 +1,50 @@
<?php
/**
* Copyright 2018 Kacper Donat
*
* @author Kacper "Kadet" Donat <kacper@kadet.net>
*
* Full license available in separate LICENSE file
*/
use Kadet\Functional as f;
class MemoizedFunctionTest extends \PHPUnit\Framework\TestCase
{
/** @var \PHPUnit\Framework\MockObject\MockObject */
private $mock;
protected function setUp()/* The :void return type declaration that should be here would cause a BC issue */
{
$this->mock = $this
->getMockBuilder('stdClass')
->setMethods(['memoized'])
->getMock();
}
public function testCalledOnlyOnce()
{
$memoized = f\memoize([$this->mock, 'memoized']);
$this->mock->expects($this->once())->method('memoized')->with();
for ($i = 0; $i < 10; $i++) {
$memoized();
}
}
public function testCalledOnlyOncePerArgs()
{
$memoized = f\memoize([$this->mock, 'memoized']);
$this->mock->expects($this->exactly(2))->method('memoized')->withConsecutive(
[ 'a' ],
[ [] ]
);
for ($i = 0; $i < 10; $i++) {
$memoized('a');
$memoized([]);
}
}
}