2013-11-16 4 views
3

Можно определить, сколько аргументов функция принимает с помощью отражения.Определить arity анонимной функции без генерации кода

Я хочу, чтобы определить функцию 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. Что означает, что он может принимать более одного аргумента. Таким образом, функция, возвращаемая compose($f, $g), может также принимать более одного аргумента - она ​​принимает точно такие же аргументы, как $g.

Проблема с этой реализацией заключается в том, что нет возможности контролировать открытую ясность того, что возвращает compose. В этом случае это всегда 1, из-за function ($x) .... При попытке определить арность с помощью отражения всегда будет возвращаться 1, а не $g.

Есть ли способ изменить количество аргументов анонимной функции, видимой системой отражения PHP, без использования eval и других методов генерации кода?

+1

Ни в коем случае я знаю – NikiC

ответ

2

В тот момент, когда вы вводите функцию func_get_args() в функцию, вы должны аннулировать любую надежду на то, что сможете определить ее истинную сущность. В этот момент арность действительно определяется только логикой функции и не может быть определена путем анализа отражения или статического кода.

Я написал compose -как реализации раньше, но это только предполагается, что функции, которые вы составляете оба имеют арность 1.

1

Мой предложил решение этой проблемы было бы введение пользовательского соглашения для указания арности с использованием настраиваемого свойства, определенного на объекте, который завершает закрытие.

Как это:

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 за указание на это.

+0

Зависит от определения «чистого» на самом деле - PHPUnit (ab) использует heck из 'eval', но люди не жалуются. ИМО - это выбор между прагматизмом и абстрактной чистотой. Приятно стремиться к чистоте, когда это возможно, но здесь? – Jon

+0

Вы не можете назначать свойства закрытию. – NikiC

+0

@NikiC вы правы. :-( – igorw

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(); 
    } 
} 
Смежные вопросы