PHP advanced functional programming-создание метода Карри, аналогичного ramdaJS


Требования:

  1. подставка держателя места
  2. Частичные прикладные функции могут быть применены к частичным прикладным функциям
  3. карринг
  4. 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 2

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));