web-dev-qa-db-ja.com

匿名の再帰PHP関数

PHP再帰的かつ匿名の関数を使用することはできますか?これは機能させるための私の試みですが、関数名では渡しません。

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

また、これが階乗を実装するのに悪い方法であることも知っています。これは単なる例です。

185
Kendall Hopkins

それが機能するためには、参照として$ factorialを渡す必要があります

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

これは単純なアプローチではないかもしれませんが、関数型言語から "fix" と呼ばれる手法について学びました。 Haskellのfix関数は、より一般的には Y Combinator として知られており、最もよく知られている 固定小数点コンビネーター の1つです。

固定小数点は、関数によって変更されない値です。関数の固定小数点fはanyx 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は実行可能なアプローチです。

24
Kendall Hopkins

実際の使用法ではありませんが、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
mpyw

PHPの新しいバージョンでは、これを行うことができます:

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

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

これにより、奇妙な動作が発生する可能性があります。

0
jgmjgm

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

0
Lei Fan