Почему моя композиция функций, реализованная при помощи Reduce, возвращает замыкание?

Я хочу получить композиционную функцию для n-функций из Reduce / Fold, но она не работает должным образом:

$id = function ($x) {
return $x;
};

$comp = function ($f) {
return function ($g) use ($f) {
return function ($x) use ($f, $g) {
return $f($g($x));
};
};
};

$fold = function ($f, $acc) {
return function ($xs) use ($f, &$acc) {
return array_reduce($xs, $f, $acc);
};
};

$compn = function($fs) {/* apply $fold here */};

$inc = function ($n) {
return $n + 1;
};

$fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields a closure instead of 3

У меня та же функция реализована в Javascript, и она работает. Я использую PHP 7.0.8 cli. Я не очень разбираюсь в PHP, поэтому я, наверное, что-то упускаю.

8

Решение

Ваш $comp является с карри, и, конечно, вы обнаружили нативный PHP array_reduce ожидает, что функция принимает несколько параметров — быстрое применение uncurry принимает немного от вашей боли, но вам нужно читать дальше, если вы хотите увидеть, как это можно улучшить в целом …


по скромному мнению PHP …

С помощью uncurry делает трюк, но вам, вероятно, не понравится ваша программа, если все функции определены как $переменные — я предвижу множество небольших проблем с использованием этого стиля.

PHP имеет подлежащий выкупу «type», который делает вещи немного более понятными — определяемые пользователем функции (включая функции высшего порядка) должны вызываться с использованием call_user_func а также call_user_func_array

namespace my\module;

function identity ($x) {
return $x;
}

function comp ($f) {
return function ($g) use ($f) {
return function ($x) use ($f, $g) {
return call_user_func ($f, call_user_func ($g, $x));
};
};
}

function uncurry ($f) {
return function ($x, $y) use ($f) {
return call_user_func (call_user_func ($f, $x), $y);
};
}

function fold ($f, $acc) {
return function ($xs) use ($f, $acc) {
return array_reduce ($xs, uncurry ($f), $acc);
};
}

Теперь ваш compn с вариадическим интерфейсом работает как положено

function compn (...$fs) {
return fold ('comp', 'identity') ($fs);
}

function inc ($x) {
return $x + 1;
}

echo compn ('inc', 'inc', 'inc') (0); // 3

Но это работает и с анонимными функциями

$double = function ($x) { return $x + $x; };

echo compn ($double, $double, 'inc') (2); // 12

функциональный код, модульная программа

С вашими функциями объявлено использование function Синтаксис, вы можете Импортировать их в других областях вашей программы

// single import
use function my\module\fold;

// aliased import
use function my\module\fold as myfold;

// multiple imports
use function my\module\{identity, comp, compn, fold};

И теперь вам не нужно засорять ваш код use-блокирует каждый раз, когда вы хотите использовать одну из своих функций

// before
$compn = function (...$fs) use ($fold, $comp, $id) {
return $fold($comp, $id) ($fs);
};

// after
function compn (...$fs) {
return fold ('comp', 'id') ($fs);
}

Когда дело доходит до отладки, несомненно, названный функции также предоставят более полезные сообщения трассировки стека


актуально, но неважно

У PHP есть другие причины для добавления подлежащий выкупу типа, но те, которые, я уверен, вас не касаются, так как они связаны с ООП — например,

вызовы метода класса

// MyClass::myFunc (1);
call_user_func (['MyClass', 'myFunc'], 1);

вызовы метода объекта

// $me->myfunc (1);
call_user_func ([$me, 'myfunc'], 1);
2

Другие решения

Я понял: array_reduce звонки $f как функция с несколькими аргументами. Поэтому я должен представить еще одну анонимную функцию:

$id = function ($x) {
return $x;
};

$comp = function ($f) {
return function ($g) use ($f) {
return function ($x) use ($f, $g) {
return $f($g($x));
};
};
};

$fold = function ($f, $acc) {
return function ($xs) use ($f, &$acc) {
return array_reduce($xs, function ($acc_, $x) use ($f) {
//                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
return $f($acc_) ($x);
}, $acc);
};
};

$compn = function($fs) {/* apply $fold here */};

$inc = function ($n) {
return $n + 1;
};

echo $fold($comp, $id) ([$inc, $inc, $inc]) (0); // yields 3

А вот оболочка Reduce / Fold, чтобы получить более приятный API:

$compn = function (...$fs) use ($fold, $comp, $id) {
return $fold($comp, $id) ($fs);
};

$compn($inc, $inc, $inc) (0);
2

По вопросам рекламы [email protected]