익명의 재귀 PHP 함수


197

재귀 적이며 익명의 PHP 함수를 사용할 수 있습니까? 이것은 작동시키려는 시도이지만 함수 이름을 전달하지 않습니다.

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

또한 이것이 계승을 구현하는 나쁜 방법이라는 것을 알고 있습니다. 이는 단지 예일뿐입니다.


확인할 PHP 5.3.0이 없지만 사용하려고 global $factorial했습니까?
kennytm

5
(참고) Lamba는 익명 함수이며 위는 Closure입니다.
Gordon

1
Lambdas와 Closure는 상호 배타적이지 않습니다. 실제로 일부 사람들은 클로저 (익명 함수)가되기 위해서는 클로저가 람다 여야한다고 생각합니다. 예를 들어 파이썬은 함수에 이름을 먼저 부여해야합니다 (버전에 따라 다름). 이름을 지정해야하기 때문에 인라인 할 수 없으며 일부는 클로저가 될 수 없다고 말할 것입니다.
Adam Gent

1
print $factorial( 0);
nickb

PHP 매뉴얼

답변:


355

작동하려면 $ factorial을 참조로 전달해야합니다.

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

이상한 bc 객체는 항상 참조와 anon으로 전달되어야합니다. 기능은 객체입니다.
ellabeauty

25
$ factorial이 전달 될 때 @ellabeauty는 여전히 null (정의되지 않음)이므로 참조로 전달해야합니다. 함수를 호출하기 전에 $ factorial을 수정하면 참조에 의해 전달 될 때 결과가 변경됩니다.
Marius Balčytis

9
@ellabeauty : 아니오, 당신은 그것을 완전히 오해합니다. 없는 모든 것은 &가치입니다. 모든 것은 &참조로입니다. "객체"는 PHP5의 값이 아니며 할당하거나 전달할 수 없습니다. 값이 객체 참조 인 변수를 다루고 있습니다. 모든 변수와 마찬가지로 변수가 있는지 여부에 따라 값 또는 참조로 캡처 할 수 있습니다 &.
newacct

3
마음을 날려! 고마워요! 지금까지 어떻게 알지 못했습니까? 재귀 익명 함수에 대한 응용 프로그램의 양은 엄청납니다. 이제 메서드를 명시 적으로 정의하고 모든 레이아웃 데이터를 클래스에서 유지하지 않고도 레이아웃에서 중첩 구조를 반복 할 수 있습니다.
Dieter Gribnitz

@barius가 말했듯이 foreach에서 사용할 때주의하십시오. $factorial함수가 호출되기 전에 변경되며 이상한 동작이 발생할 수 있습니다.
STIL

24

나는 이것이 단순한 접근법이 아닐 수도 있다는 것을 알고 있지만, 기능 언어에서 "수정" 이라는 기술에 대해 배웠다 . fixHaskell 의 기능은 일반적으로 가장 잘 알려진 고정 소수점 조합 중 하나 인 Y 결합기로 알려져 있습니다 .

고정 소수점 함수에 의해 변경되지 않은 값이다 함수의 고정 점 F는 임의이다 X 되도록 X = F (X). 고정 소수점 조합기 y 는 모든 함수 f에 대한 고정 소수점을 반환하는 함수입니다. y (f)는 고정 소수점 f이므로 y (f) = f (y (f))입니다.

기본적으로 Y 결합기는 원래의 모든 인수와 재귀 함수 인 추가 인수를 취하는 새로운 함수를 만듭니다. 이것이 작동하는 방식은 카레 표기법을 사용하여 더 분명합니다. 괄호 ( f(x,y,...))로 인수를 쓰는 대신 함수 뒤에 씁니다 f x y .... Y 조합기는 다음과 같이 정의된다 Y f = f (Y f). 또는 재귀 함수에 대한 단일 인수로 Y f x = f (Y f) x.

PHP는 자동으로 함수를 카레 하지 않기 때문에 약간의 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 결합기는 실행 가능한 접근법입니다.


10
그것은 call_user_func_array()크리스마스만큼 느리다는 점 은 주목할 가치가 있습니다.
Xeoncross

11
@Xeoncross 육상 속도 기록을 설정하는 다른 PHP와 달리? : P
Kendall Hopkins

1
이제 (5.6+) 대신 인수 압축 풀기를 사용할 수 있습니다 call_user_func_array.
Fabien Sa

@KendallHopkins 왜 추가 인수 array_unshift( $args, fix($func) );인가? Args에는 이미 매개 변수가 포함되어 있으며 실제 재귀는 call_user_func_array ()에 의해 수행되므로 해당 줄은 무엇을합니까?
답변을 원합니다

5

실용적이지는 않지만 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)

0

최신 버전의 PHP에서는 다음을 수행 할 수 있습니다.

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

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

이것은 잠재적으로 이상한 행동으로 이어질 수 있습니다.


0

PHP 7.1 이상에서 Y Combinator를 다음과 같이 사용할 수 있습니다.

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


0

변수를 정의하지 않고 익명 클래스 (PHP 7+)를 사용하는 경우 :

echo (new class {
    function __invoke($n) {
        return $n < 2 ? 1 : $n * $this($n - 1);
    }
})(5);
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.