Подтвердить что ты не робот

Анонимные рекурсивные функции PHP

Возможно ли иметь функцию PHP, которая является как рекурсивной, так и анонимной? Это моя попытка заставить его работать, но он не передается в имени функции.

$factorial = function( $n ) use ( $factorial ) {
    if( $n <= 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );

Я также знаю, что это плохой способ реализовать факториал, это просто пример.

4b9b3361

Ответ 1

Чтобы он работал, вам нужно передать $factorial в качестве ссылки

$factorial = function( $n ) use ( &$factorial ) {
    if( $n == 1 ) return 1;
    return $factorial( $n - 1 ) * $n;
};
print $factorial( 5 );

Ответ 2

Я знаю, что это может быть не простой подход, но я узнал о методе "исправить" с функциональных языков. Функция fix от Haskell известна более широко как Y combinator, которая является одной из самых известных комбинаторы с фиксированной запятой.

Фиксированная точка - это значение, которое не изменяется функцией: неподвижная точка функции f является любой x такой, что x = f (x). Комбинатор с фиксированной точкой y является функцией, которая возвращает неподвижную точку для любой функции f. Так как y (f) - фиксированная точка f, то y (f) = f (y (f)).

По существу, Y combinator создает новую функцию, которая принимает все аргументы оригинала плюс дополнительный аргумент, который является рекурсивной функцией. Как это работает, более очевидно, используя кардиотрансляцию. Вместо записи аргументов в круглых скобках (f(x,y,...)), напишите их после функции: f x y .... Y combinator определяется как Y f = f (Y f); или, с единственным аргументом для рекурсированной функции, Y f x = f (Y f) x.

Так как PHP не автоматически выполняет функции curry, это немного взломать, чтобы сделать fix работу, но я думаю, что это интересно.

function fix( $func )
{
    return function() use ( $func )
    {
        $args = func_get_args();
        array_unshift( $args, fix($func) );
        return call_user_func_array( $func, $args );
    };
}

$factorial = function( $func, $n ) {
    if ( $n == 1 ) return 1;
    return $func( $n - 1 ) * $n;
};
$factorial = fix( $factorial );

print $factorial( 5 );

Обратите внимание, что это почти то же самое, что и простые решения по закрытию, опубликованные другими пользователями, но функция fix создает для вас закрытие. Комбинаторы с фиксированной точкой немного сложнее, чем использование замыкания, но являются более общими и имеют другое применение. Хотя метод закрытия более подходит для PHP (который не является ужасно функциональным языком), исходная проблема - это скорее упражнение, чем для производства, поэтому Y combinator является жизнеспособным подходом.

Ответ 3

Хотя это не для практического использования, расширение уровня C mpyw-junks/phpext-callee обеспечивает анонимную рекурсию без назначения переменных.

<?php

var_dump((function ($n) {
    return $n < 2 ? 1 : $n * callee()($n - 1);
})(5));

// 5! = 5 * 4 * 3 * 2 * 1 = int(120)

Ответ 4

В новых версиях PHP вы можете сделать это:

$x = function($depth = 0) {
    if($depth++)
        return;

    $this($depth);
    echo "hi\n";
};
$x = $x->bindTo($x);
$x();

Это может привести к странному поведению.

Ответ 5

Вы можете использовать Y Combinator в PHP 7. 1+, как показано ниже:

function Y
($le)
{return
    (function ($f) 
     {return
        $f($f);
     })(function ($f) use ($le) 
        {return
            $le(function ($x) use ($f) 
                {return
                    $f($f)($x);
                });
        });
}

$le =
function ($factorial)
{return
    function
    ($n) use ($factorial)
    {return
        $n < 2 ? $n
        : $n * $factorial($n - 1);
    };
};

$factorial = Y($le);

echo $factorial(1) . PHP_EOL; // 1
echo $factorial(2) . PHP_EOL; // 2
echo $factorial(5) . PHP_EOL; // 120

Поиграй с ним: https://3v4l.org/7AUn2

Исходники от: https://github.com/whitephp/the-little-phper/blob/master/src/chapter_9.php