2015-05-29 13 views
12

aşağıdaki C# fonksiyonu:Ekstra ldnull ve kuyruğun amacı nedir. F # uygulamasında vs C#?

IL_0000: ldarg.1  
IL_0001: callvirt 05 00 00 0A 
IL_0006: ret 

Ama eşdeğer F # işlevi:

let resultOfFunc func = func() 

Buna derler:

T ResultOfFunc<T>(Func<T> f) 
{ 
    return f(); 
} 

Buna şaşırtıcı olmayan derler

IL_0000: nop   
IL_0001: ldarg.0  
IL_0002: ldnull  
IL_0003: tail.  
IL_0005: callvirt 04 00 00 0A 
IL_000A: ret 

(Her ikisi de serbest bırakma modunda). Başlangıçta fazla merak etmediğim fazladan bir nop var, ama ilginç olan şey ek ldnull ve tail. yönergeleri. (Muhtemelen yanlış)

Benim tahminim ldnull fonksiyonu bu yüzden hala bir şey (unit) döndürür void olması durumunda gereklidir, ama bu tail. eğitimin amacı ne olduğunu açıklamak olmamasıdır. Ve eğer fonksiyon yığın üzerinde bir şey itiyorsa ne olur, atılmayan ekstra bir null ile sıkışıp kalmaz mı?

+3

Bu durumda fonksiyonun bir kuyruk çağrısına dönüştürüldüğünden şüpheleniyorum - yeni işlev eski bir yığının yığılma alanını kullanacak –

+1

Bunun bir performans sıkıntısına neden olacağını unutmayın, bu soruya bakın: [Performans Generic.List .Add bir fonksiyondaki son ifadedir ve tailcall optimizasyonu açıksa] (http://stackoverflow.com/q/28649422/636019) – ildjarn

cevap

17

C# ve F # sürümlerinin önemli bir ayrımı vardır: C# işlevinde herhangi bir parametre yoktur, ancak F # sürümünde unit türünde bir parametre vardır. Bu unit değeri, ldnull olarak gösterilendir (null, yalnızca unit değerinin, () öğesinin gösteriminde kullanılıyor). Eğer C# ikinci işlevi çevirmek için olsaydı

, bu şekilde görünecektir: .tail talimat gelince

T ResultOfFunc<T>(Func<Unit, T> f) { 
    return f(null); 
} 

- böylece denir "kuyruk çağrı optimizasyonu".
Düzenli bir işlev çağrısı sırasında, bir geri dönüş adresi yığına (CPU yığını) aktarılır ve daha sonra işlev çağrılır. Fonksiyon bittiğinde, iade adresini yığıntan dışarı çıkaran ve kontrolleri oraya transfer eden "dönüş" talimatını yürütür. fonksiyon A çağrıları B işlev ve ne zaman
Ancak, daha sonra hemen başka bir şey yapmadan, fonksiyon B 'ın dönüş değerini verir, CPU yığının üzerine ekstra dönüş adresini iterek atlayın ve B bir 'atlayışı' yapabilir "çağrı" yerine. Bu şekilde, B "geri dönüş" komutunu çalıştırdığında, CPU yığından geri dönüş adresini atar ve bu adres A'a değil, ilk etapta A'a işaret eder. fonksiyon A çağrıları dönmeden önce B değil çalışacaktır, fakat dönen yerine ait ve B dönen böylece delegeler onur:
bunu düşünmek için başka bir yoldur.

Bu nedenle, bu sihirli teknik, numaralı yığında bir nokta tüketmeden numaralı telefonu aramamıza olanak sağlar; bu da, yığın taşması riski olmadan, bu tür çağrıların çoğunu keyfi olarak gerçekleştirebileceğiniz anlamına gelir. Bu, fonksiyonel programlamada çok önemlidir, çünkü özyineli algoritmaların verimli bir şekilde uygulanmasına izin verir.kuyruk A ait de O "kuyruk çağrısı" denir

, B çağrısı olur, çünkü bunu söylemek, .