为什么我的函数组合通过reduce returns实现了一个闭包?
Why does my function composition implemented by reduce returns a closure?
我想从 reduce/fold 导出 n 函数的组合函数,但它没有按预期工作:
$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,所以我可能忽略了一些东西。
我想通了: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);
你的$comp
是curried,你当然发现了PHP的原生array_reduce
期望函数接受多个参数——[=的快速应用19=] 可以减轻 一些 的痛苦,但如果您想了解如何从整体上改善这一点,则需要继续阅读...
在PHP的愚见...
使用 uncurry 可以解决问题,但如果所有函数都定义为 $
命名变量,您可能最终会不喜欢您的程序 – 我预见很多使用那种风格的小问题。
PHP 有一个 callable "type" which makes things a little more PHP-ish – user-defined functions (including higher-order functions) should be called using call_user_func and 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
语法声明的函数,您可以 import 它们进入程序的其他区域
// 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);
}
在调试方面,毫无疑问,named 函数也会提供更有帮助的堆栈跟踪消息
相关但不重要
PHP 添加 callable 类型还有其他原因,但我相信您不会关心这些,因为它们与 OOP 相关——例如,
class 方法调用
// MyClass::myFunc (1);
call_user_func (['MyClass', 'myFunc'], 1);
对象方法调用
// $me->myfunc (1);
call_user_func ([$me, 'myfunc'], 1);
我想从 reduce/fold 导出 n 函数的组合函数,但它没有按预期工作:
$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,所以我可能忽略了一些东西。
我想通了: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);
你的$comp
是curried,你当然发现了PHP的原生array_reduce
期望函数接受多个参数——[=的快速应用19=] 可以减轻 一些 的痛苦,但如果您想了解如何从整体上改善这一点,则需要继续阅读...
在PHP的愚见...
使用 uncurry 可以解决问题,但如果所有函数都定义为 $
命名变量,您可能最终会不喜欢您的程序 – 我预见很多使用那种风格的小问题。
PHP 有一个 callable "type" which makes things a little more PHP-ish – user-defined functions (including higher-order functions) should be called using call_user_func and 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
语法声明的函数,您可以 import 它们进入程序的其他区域
// 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);
}
在调试方面,毫无疑问,named 函数也会提供更有帮助的堆栈跟踪消息
相关但不重要
PHP 添加 callable 类型还有其他原因,但我相信您不会关心这些,因为它们与 OOP 相关——例如,
class 方法调用
// MyClass::myFunc (1);
call_user_func (['MyClass', 'myFunc'], 1);
对象方法调用
// $me->myfunc (1);
call_user_func ([$me, 'myfunc'], 1);