2010-03-19 25 views
150

Hem özyinelemeli hem de anonim bir PHP işlevine sahip olmak mümkün mü? Bu, onu işe alma girişimimdir, ancak işlev adından geçmez.Anonim özyinelemeli PHP fonksiyonları

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

Ayrıca bunun, fakültenin uygulanmasının kötü bir yolu olduğunu da biliyorum, bu sadece bir örnek. o iş için Amacıyla

+0

Kontrol etmek için PHP 5.3.0 yok, ama 'global $ factorial' kullanmayı denediniz mi? – kennytm

+4

* (sidenote) * a Lamba, anonim bir fonksiyondur. – Gordon

+1

Lambdalar ve Kapanışlar karşılıklı olarak münhasır değildir. Aslında bazı insanlar bir kapanışın bir kapama (anonim işlev) olması için lambda olması gerektiğine inanırlar. Örneğin, sıraladığınız Python işlevi ilk önce bir ada sahip olmak zorundadır (versiyona bağlı olarak).Çünkü ona ismini veremediğin bir isim vermelisin ve bazıları bunu bir kapanıştan mahrum bırakıyor. –

cevap

280

, size $ Benden bu basit bir yaklaşım olmayabilir biliyorum referans

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

+1, ayrıca http: //php100.wordpress .com/2009/04/13/php-y-combinator/ – user187291

+0

Bu garip bc nesneleri her zaman referans ve anon tarafından geçirilmelidir. işlevler nesnelerdir ... – ellabeauty

+20

@ellabeauty zaman içinde $ factorial geçmiştir, hala null (tanımlı değil), işte bu yüzden başvurmak zorundasınız. Dikkat edin, eğer fonksiyonu çağırmadan önce $ faktörünü değiştirirseniz, sonuç referans olarak geçtikçe değişecektir. –

22

olarak faktöryel gerekir ama fonksiyonel dillerden "fix" denilen bir teknikle öğrendik. Haskell'den fix fonksiyonu, daha genel olarak bilinen en iyi fixed point combinators'dan biri olan Y combinator olarak bilinir.

bir sabit nokta bir işlev tarafından değişmez bir değerdir: f fonksiyonunun sabit bir noktada herhangi bir x, x   =   f (x) tanımlanmaktadır. Bir sabit nokta birleştirici y, herhangi bir işlev f için sabit bir nokta döndüren bir işlevdir. Y (f) sabit bir f noktası olduğu için, y (f)   =   f (y (f)) değerine sahibiz.

Esasen Y birleştiricisi, orijinalin tüm bağımsız değişkenlerini ve ayrıca özyinelemeli işlevin ek bir argümanını alan yeni bir işlev oluşturur. Bu nasıl çalışır kıvrımlı notasyon kullanılarak daha açıktır. Parantez içinde bağımsız değişkenler yazmak yerine (f(x,y,...)), işlevden sonra bunları yazın: f x y .... Y birleştiricisi, Y f = f (Y f); veya, tekrarlanan fonksiyon için tek bir argüman ile, Y f x = f (Y f) x.

PHP otomatik olarak curry işlevlerini içermediğinden, fix işini yapmak biraz kesintidir, ancak bunun ilginç olduğunu düşünüyorum.

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); 

Not bu neredeyse diğerleri gönderdiniz basit kapama çözümleri ile aynıdır, ancak işlevi fix sizin için kapatma yaratır. Sabit nokta kombinatorörleri, bir kapak kullanmaktan biraz daha karmaşıktır, fakat daha geneldir ve başka kullanımları vardır. Kapatma yöntemi PHP için daha uygunken (ki bu çok işlevsel bir dil değildir), orijinal sorun üretimden daha fazladır, dolayısıyla Y birleştiricisi uygulanabilir bir yaklaşımdır.

+8

'call_user_func_array() 'Noel kadar yavaş olduğunu belirtmekte fayda var. – Xeoncross

+10

@Xeoncross Arazi hız rekorunu kıran PHP'nin tersine mi? : P –

+1

Not, şimdi (5.6+) 'call_user_func_array' yerine argüman açmayı kullanabilirsiniz. –

2

Pratik kullanım için olmasa da, mpyw-junks/phpext-callee C düzeyi uzantı değişkenleri atamadan anonim yineleme sağlar.

<?php 

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

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

PHP yeni sürümlerinde bunu yapabilirsiniz:

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

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

Bu potansiyel garip davranışlara yol açabilir.