可以通过使用反射确定函数接受的参数数量。
我希望能够定义执行函数组合的函数compose
。也就是说,compose($f, $g)
应该生成一个返回$f($g($x))
的新函数。
我在这里有一个示例实现:
function compose()
{
$fns = func_get_args();
$prev = array_shift($fns);
foreach ($fns as $fn) {
$prev = function ($x) use ($fn, $prev) {
$args = func_get_args();
return $prev(call_user_func_array($fn, $args));
};
}
return $prev;
}
撰写$f
和$g
时,$g
可能有一个高于1的arity。这意味着它可能需要多个参数。因此,compose($f, $g)
返回的函数也可能需要多个参数 - 它与$g
采用完全相同的参数。
此实现的问题在于无法控制compose
返回的公开arity。在这种情况下,由于1
,它始终为function ($x) ...
。在尝试使用反射确定arity时,它将始终返回1
而不是$g
。
有没有办法在不使用eval
和其他代码生成技术的情况下更改PHP反射系统看到的匿名函数的参数数量?
答案 0 :(得分:2)
将func_get_args()
引入函数的那一刻应该会使任何能够确定其真正arity的希望无效。那时,arity实际上只是由函数的逻辑定义,不能通过反射或静态代码分析来确定。
之前我写了一个类似compose
的实现,但它只是假设你正在编写的函数都具有1的arity。
答案 1 :(得分:2)
这是一个丑陋的解决方案,因为它不适用于所有数量的参数(或者你最终有大量case
个东西),但它不依赖于eval
:
function compose($f, $g)
{
switch(getReflectiveArity($g)) {
case 1:
return function($x) use ($f, $g) {
return $f($g($x));
};
break;
case 2:
return function($x, $y) use ($f, $g) {
return $f($g($x));
};
break;
case 3:
return function($x, $y, $z) use ($f, $g) {
return $f($g($x, $y, $z));
};
break;
/* ... */
default:
throw new RuntimeException();
}
}
答案 2 :(得分:1)
我建议的解决方案是引入一个自定义约定,用于使用自定义属性指定arity,该属性在包装闭包的对象上定义。
像这样:
class CustomArityFunction
{
public $f;
public $arity;
function __construct(callable $f, $arity)
{
$this->f = $f;
$this->arity = $arity;
}
function __invoke()
{
return call_user_func_array($this->f, func_get_args());
}
}
// define function
$f = function () { ... };
return new CustomArityFunction($f, $n);
// determine arity
$arity = ($f instanceof CustomArityFunction) ? $f->arity : getReflectiveArity($f);
此解决方案的主要缺点是消费代码需要了解惯例。
然而,这是我能做到的最干净的方式。
注意:需要包装器,因为PHP不允许将属性分配给闭包。感谢@nikic指出这一点。
答案 3 :(得分:0)
基于How can I dynamically check the number of arguments expected of an anonymous function in PHP? 是以下一段代码,适用于php> = 5.3
更新:重新阅读问题......这可能是BS