PHP advanced functional programming-создание метода Карри, аналогичного ramdaJS
Требования:
- подставка держателя места Частичные прикладные функции могут быть применены к частичным прикладным функциям
- карринг
- 5.6 поддержка PHP
Вот моя попытка, однако она поддерживает только hhvm 3.7, как вы можете видеть в Примере на http://3v4l.org/0i5FV
<?php
class Placeholder{}
function curry(callable $func, ... $curriedArgs) {
return function(...$fulfillment) use ($func, $curriedArgs) {
$removedPlaceheldArgs = array_map(function($arg) use ($fulfillment) {
if ($arg instanceof Placeholder)
return array_shift($fulfillment);
return $arg;
}, $curriedArgs);
return $func(...array_merge($removedPlaceheldArgs, $fulfillment));
};
};
Вот пример моего использования:
$resultset = json_decode('[{
"id": 1,
"gender": "Male",
"name": "Matthew"
}, {
"id": 2,
"gender": "Male",
"name": "Willie"
}, {
"id": 3,
"gender": "Female",
"name": "Ann"
}, {
"id": 4,
"gender": "Female",
"name": "Margaret"
}, {
"id": 5,
"gender": "Female",
"name": "Marie"
}]',TRUE);
$filterGender = function($row,$gender){
return $row['gender']==$gender;
};
$detectMen = curry($filterGender,(new Placeholder),'Male');
$detectWomen = curry($filterGender,(new Placeholder),'Female');
var_dump($detectMen(['gender'=>'Male'])); //works -- expected result TRUE
$getLadies = curry('array_filter',(new Placeholder),$detectWomen);
var_dump($getLadies($resultset)); //does not work -- expected result Ladies from result set
Можно ли получить результат $getLadies в PHP 5.6 вместо простого hhvm?
2 ответа:
Это не очень красиво, но это должно быть близкой эмуляцией текущей функции Рамды
curry
.namespace Phamda { function _() { static $placeholder; if ($placeholder === null) { $placeholder = new \stdClass; } return $placeholder; } function curryN($n, $f) { $curryNRec = function($recv) use ($n, $f, &$curryNRec) { return function () use ($recv, $n, $f, &$curryNRec) { $left = $n; $argsIdx = 0; $combined = array(); $combindedIdx = 0; $args = func_get_args(); while ($combindedIdx < count($recv) || $argsIdx < count($args)) { if ($combindedIdx < count($recv) && ($recv[$combindedIdx] !== _() || $argsIdx > count($args))) { $result = $recv[$combindedIdx]; } else { $result = $args[$argsIdx]; $argsIdx += 1; } $combined[$combindedIdx] = $result; $combindedIdx += 1; if ($result !== _()) { $left -= 1; } } if ($left <= 0) { return call_user_func_array($f, $combined); } else { return $curryNRec($combined); } }; }; return $curryNRec([]); } function curry($f) { $fRefl = new \ReflectionFunction($f); return curryN($fRefl->getNumberOfParameters(), $f); } }
И используя Ваш пример:
use Phamda as P; $resultset = json_decode('[{ "id": 1, "gender": "Male", "name": "Matthew" }, { "id": 2, "gender": "Male", "name": "Willie" }, { "id": 3, "gender": "Female", "name": "Ann" }, { "id": 4, "gender": "Female", "name": "Margaret" }, { "id": 5, "gender": "Female", "name": "Marie" }]', true); $filterGender = P\curry(function($row, $gender){ return $row['gender'] == $gender; }); $detectMen = $filterGender(P\_(), 'Male'); $detectWomen = $filterGender(P\_(), 'Female'); var_dump($detectMen(['gender' => 'Male'])); // bool(true) $filter = P\curry('array_filter'); $getLadies = $filter(P\_(), $detectWomen); var_dump($getLadies($resultset)); /* array(3) { [2] => array(3) { 'id' => int(3) 'gender' => string(6) "Female" 'name' => string(3) "Ann" } [3] => array(3) { 'id' => int(4) 'gender' => string(6) "Female" 'name' => string(8) "Margaret" } [4] => array(3) { 'id' => int(5) 'gender' => string(6) "Female" 'name' => string(5) "Marie" } } */
Вы можете использовать частичное применение и функции карри из нестандартной библиотеки PHP :
use function nspl\f\rpartial; $detectMen = rpartial($filterGender, 'Male'); $detectWomen = rpartial($filterGender, 'Female'); var_dump($detectMen(array('gender' => 'Male'))); $getLadies = rpartial('array_filter', $detectWomen); var_dump($getLadies($resultset));