在没有代码生成的情况下定义匿名函数的arity

时间:2013-11-16 18:53:36

标签: php

可以通过使用反射确定函数接受的参数数量。

我希望能够定义执行函数组合的函数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反射系统看到的匿名函数的参数数量?

4 个答案:

答案 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

http://3v4l.org/6KPFN

更新:重新阅读问题......这可能是BS