İnternette bu soru için zaten birçok harika cevap var. Konu hakkında topladığım çeşitli açıklamaların ve örneklerin bir derlemesini yazacağım.
GİRİŞ
değere göre çağrı (CBV)
Tipik olarak, fonksiyonlara ait parametreler, çağrı-değer parametreleri; yani, fonksiyonun kendisi değerlendirilmeden önce değerlerini belirlemek için parametreler soldan sağa değerlendirilir
def first(a: Int, b: Int): Int = a
first(3 + 4, 5 + 6) // will be reduced to first(7, 5 + 6), then first(7, 11), and then 7
adıyla çağrı (CBN)
Ancak, parametre olarak kabul eden bir işlevi, fonksiyonumuz içinde çağrılıncaya kadar değerlendirmediğimiz bir ifade yazmamız gerekirse ne olur? Bu durumda, Scala isimle çağrı parametreleri sunar. Yani parametre fonksiyona olduğu gibi aktarılır ve değerlemesi ikame işleminden sonra gerçekleşir
def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7
İsme göre çağrı mekanizması, çağrıya bir kod bloğu iletir ve çağrı parametreye her eriştiğinde, kod bloğu yürütülür ve değer hesaplanır. Aşağıdaki örnekte, gecikmeli olarak yöntemin girildiğini gösteren bir mesaj yazdırılır. Ardından, gecikmeli değeri olan bir ileti yazdırır. Son olarak, gecikmeli 't' döndürür:
object Demo {
def main(args: Array[String]) {
delayed(time());
}
def time() = {
println("Getting time in nano seconds")
System.nanoTime
}
def delayed( t: => Long ) = {
println("In delayed method")
println("Param: " + t)
}
}
Gecikmeli yöntem
Nano saniyede zaman alma
Param: 2027245119786400
HER DURUM İÇİN PROS VE İNŞAAT
CBN:
+ Daha sık sona erer * sonlandırmanın altında kontrol edin * + İlgili gövdenin değerlendirilmesinde karşılık gelen parametre kullanılmazsa bir işlev bağımsız değişkeninin değerlendirilmemesi avantajına sahiptir. - Daha yavaştır, daha fazla sınıf oluşturur (yani program alır yüklemek için daha uzun) ve daha fazla bellek tüketir.
CBV:
+ Genellikle CBN'den üstel olarak daha verimlidir, çünkü isimle çağrılan argüman ifadelerinin tekrar tekrar hesaplanmasını önler. Her işlev argümanını yalnızca bir kez değerlendirir + Zorunlu efektler ve yan etkilerle çok daha güzel oynar, çünkü ifadelerin ne zaman değerlendirileceğini daha iyi bilirsiniz. - Parametrelerin değerlendirilmesi sırasında bir döngüye neden olabilir * yukarıdaki sonlandırmayı kontrol edin *
Fesih garanti edilmezse ne olur?
-Bir ifadenin CBV değerlendirmesi sona ererse, e'nin CBN değerlendirmesi de sona erer -Diğer yön doğru değildir
Fesih olmayan örnek
def first(x:Int, y:Int)=x
İlk önce ifadeyi düşünün (1, döngü)
CBN: ilk (1, döngü) → 1 CBV: ilk (1, döngü) → bu ifadenin argümanlarını azaltın. Bir döngü olduğu için argümanları sonsuz bir şekilde azaltır. Sonlandırılmaz
HER DURUM DAVRANIŞINDAKİ FARKLAR
Şimdi bir yöntem testi tanımlayalım.
Def test(x:Int, y:Int) = x * x //for call-by-value
Def test(x: => Int, y: => Int) = x * x //for call-by-name
Vaka1 testi (2,3)
test(2,3) → 2*2 → 4
Önceden değerlendirilmiş bağımsız değişkenlerle başladığımızdan beri, bu, değere göre çağrı ve ada göre çağrı için aynı miktarda adım olacaktır.
Vaka2 testi (3 + 4,8)
call-by-value: test(3+4,8) → test(7,8) → 7 * 7 → 49
call-by-name: (3+4)*(3+4) → 7 * (3+4) → 7 * 7 → 49
Bu durumda, değere göre çağrı daha az adım gerçekleştirir
Case3 testi (7, 2 * 4)
call-by-value: test(7, 2*4) → test(7,8) → 7 * 7 → 49
call-by-name: (7)*(7) → 49
İkinci argümanın gereksiz hesaplanmasından kaçınırız
Case4 testi (3 + 4, 2 * 4)
call-by-value: test(7, 2*4) → test(7,8) → 7 * 7 → 49
call-by-name: (3+4)*(3+4) → 7*(3+4) → 7*7 → 49
Farklı yaklaşım
İlk olarak, yan etkisi olan bir fonksiyonumuz olduğunu varsayalım. Bu işlev bir şey yazdırır ve ardından bir Int döndürür.
def something() = {
println("calling something")
1 // return value
}
Şimdi, tamamen bağımsız olan Int bağımsız değişkenlerini kabul eden iki işlev tanımlayacağız, ancak biri bağımsız değişkeni bir çağrı-değer stilinde (x: Int), diğerini de çağrı-çağrı stilinde (x: => Int).
def callByValue(x: Int) = {
println("x1=" + x)
println("x2=" + x)
}
def callByName(x: => Int) = {
println("x1=" + x)
println("x2=" + x)
}
Şimdi onları yan etki fonksiyonumuzla çağırdığımızda ne olur?
scala> callByValue(something())
calling something
x1=1
x2=1
scala> callByName(something())
calling something
x1=1
calling something
x2=1
Böylece, çağrı-değer sürümünde, aktarılan işlev çağrısının (bir şey ()) yan etkisinin yalnızca bir kez gerçekleştiğini görebilirsiniz. Ancak, adıyla çağrı sürümünde, yan etki iki kez oldu.
Bunun nedeni, çağrı-değer işlevlerinin, işlevi çağırmadan önce iletilen ifadenin değerini hesaplamasıdır, böylece her seferinde aynı değere erişilir. Ancak, adıyla çağrı işlevleri, her erişildiğinde içeri aktarılan ifadenin değerini yeniden hesaplar.
ADI ÇAĞRI KULLANMAK İÇİN İYİ OLDUĞU ÖRNEKLER
Gönderen: https://stackoverflow.com/a/19036068/1773841
Basit performans örneği: günlüğe kaydetme.
Bunun gibi bir arayüz düşünelim:
trait Logger {
def info(msg: => String)
def warn(msg: => String)
def error(msg: => String)
}
Ve sonra şöyle kullanılır:
logger.info("Time spent on X: " + computeTimeSpent)
Bilgi yöntemi hiçbir şey yapmazsa (örneğin, günlük kaydı düzeyi bundan daha yüksek bir değere yapılandırılmışsa), computeTimeSpent hiçbir zaman çağrılmaz ve zaman kazandırır. Bu genellikle kaydedilen görevlere göre pahalı olabilen dize manipülasyonu gördüğü kaydedicilerde çok olur.
Doğruluk örneği: mantık operatörleri.
Muhtemelen böyle bir kod gördünüz:
if (ref != null && ref.isSomething)
&& yöntemini şöyle beyan edeceğinizi düşünün:
trait Boolean {
def &&(other: Boolean): Boolean
}
ref her null olduğunda, bir hata alırsınız çünkü && öğesine geçmeden önce isSomething nullreference üzerinden çağrılır. Bu nedenle asıl açıklama:
trait Boolean {
def &&(other: => Boolean): Boolean =
if (this) this else other
}
=> Int
Farklı bir türü arasındanInt
;Int
sadece vs üretecek argümanların fonksiyonuInt
. Eğer birinci sınıf işlevleri var kez yok gerek bunu tarif etmeye çağrı isme göre terminoloji icat.