Scala'da isme göre arama veya değere göre arama, açıklama gerekiyor


239

Anladığım kadarıyla Scala'da bir fonksiyon da

  • by-value veya
  • isimle

Örneğin, aşağıdaki beyanlar göz önüne alındığında, işlevin nasıl çağrılacağını biliyor muyuz?

Beyan:

def  f (x:Int, y:Int) = x;

Aramak

f (1,2)
f (23+55,5)
f (12+3, 44*11)

Kurallar neler lütfen?

Yanıtlar:


540

Verdiğiniz örnek yalnızca çağrı değeri kullanır, bu yüzden farkı gösteren yeni, daha basit bir örnek vereceğim.

İlk olarak, yan etkisi olan bir fonksiyonumuz olduğunu varsayalım. Bu işlev bir şey yazdırır ve ardından bir döndürür Int.

def something() = {
  println("calling something")
  1 // return value
}

Şimdi Int, biri argümanı bir çağrı-değer stilinde ( x: Int) diğerini de çağrı-çağrı stilinde ( ) almak dışında, tamamen aynı olan argümanları kabul eden iki işlev tanımlayacağız 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 ( something()) 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. Bunun yerine, adıyla çağrı işlevleri , her erişildiğinde geçirilen ifadenin değerini yeniden hesaplar .


296
Her zaman bu terminolojinin gereksiz yere kafa karıştırıcı olduğunu düşündüm. Bir işlev, ada göre arama değerlerine göre değişen birden fazla parametreye sahip olabilir. Bir o değil Yani işlev çağrısı-by-name veya çağrı bazında değer olduğunu, kendi her işte parametreler olabilir geçmesi -by-adını veya pass-by-value. Ayrıca, "isim-adı" nın isimlerle bir ilgisi yoktur . => IntFarklı bir türü arasından Int; Intsadece vs üretecek argümanların fonksiyonu Int. Eğer birinci sınıf işlevleri var kez yok gerek bunu tarif etmeye çağrı isme göre terminoloji icat.
Ben

2
@Ben, bu birkaç soruyu cevaplamaya yardımcı oluyor, teşekkürler. Keşke daha fazla yazı yazmanın bu adla anlambilim anlamını açık bir şekilde açıklamasını isterdim.
Mart'ta Christopher Poile

3
@SelimOber Metin f(2)tür ifadesi olarak derlenmişse Int, oluşturulan kod fargümanla çağrılır 2ve sonuç ifadenin değeridir. Aynı metin bir tür ifadesi olarak derlenmişse => Int, oluşturulan kod ifadenin değeri olarak bir tür "kod bloğu" na bir referans kullanır. Her iki durumda da, o tür bir değer, o tür bir parametre bekleyen bir işleve geçirilebilir. Görünürde parametre geçmeden değişken atama ile bunu yapabileceğinizden eminim. Peki isimler veya aramaların bununla ne ilgisi var?
Ben

4
@Ben Peki => Int"bir Int üreten hiçbir argüman işlevi" ise, nasıl farklı () => Int? Scala bunlara farklı davranıyor gibi görünüyor, örneğin => Intgörünüşe göre a tipi olarak değil val, sadece bir parametre tipi olarak çalışıyor.
Tim Goodman

5
@TimGoodman Haklısın, yaptığımdan biraz daha karmaşık. => Intbir kolaylıktır ve tam olarak bir işlev nesnesi olarak uygulanmaz (muhtemelen neden değişkenler olamazsınız => Int, ancak bunun çalışamamasının temel bir nedeni yoktur). () => Int, açıkça döndürülmesi Intgereken ve açıkça çağrılması gereken ve bir işlev olarak iletilebilen hiçbir argümanın bulunmadığı bir işlevdir. => Intbir tür "proxy Int" ve onunla yapabileceğiniz tek şey (dolaylı olarak) almak için çağırmaktır Int.
Ben

51

İşte Martin Odersky'den bir örnek:

def test (x:Int, y: Int)= x*x

Değerlendirme stratejisini incelemek ve bu koşullarda hangisinin daha hızlı (daha az adım) olduğunu belirlemek istiyoruz:

test (2,3)

değere göre arama: test (2,3) -> 2 * 2 -> 4
ada göre arama: test (2,3) -> 2 * 2 -> 4
Burada sonuca aynı sayıda adımla ulaşılır.

test (3+4,8)

değere göre arama: test (7,8) -> 7 * 7 -> 49
ada göre arama: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
İşte çağrı değerine göre daha hızlı.

test (7,2*4)

değere göre arama: test (7,8) -> 7 * 7 -> 49
ada göre arama: 7 * 7 -> 49
Burada isme göre arama daha hızlı

test (3+4, 2*4) 

değere göre arama: test (7,2 * 4) -> test (7, 8) -> 7 * 7 -> 49
ada göre arama: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Sonuçlara aynı adımlarla ulaşılır.


1
CBV için üçüncü örnekte, sanırım test (7,14) yerine test (7,8) demek
istediniz

1
Örnek, scala programlamanın prensibi Coursera'dan alınmıştır. Anlatım 1.2. Ada göre arama, def test (x:Int, y: => Int) = x * xy parametresinin asla kullanılmadığına dikkat etmelidir .
dr jerry

1
İyi örnek! Coursera MOOC'den alınmıştır :)
alxsimo

Bu farkın iyi bir açıklamasıdır, ancak sorulan soruya değinmez, yani ikisinden hangisi Scala çağırıyor
db1234 18:17

16

Örneğinizde, tüm parametreler işlevde çağrılmadan önce değerlendirilir , çünkü bunları yalnızca değere göre tanımlarsınız . Parametrelerinizi ada göre tanımlamak istiyorsanız, bir kod bloğu geçirmelisiniz:

def f(x: => Int, y:Int) = x

Bu yolla parametre işlevde xçağrılıncaya kadar değerlendirilmez .

Buradaki bu küçük yazı bunu da güzel açıklıyor.


10

Yukarıdaki yorumlarda @ Ben'in noktasını yinelemek için, "isimle çağrı" yı sadece sözdizimsel şeker olarak düşünmenin en iyisi olduğunu düşünüyorum. Ayrıştırıcı ifadeleri anonim işlevlere sarar, böylece kullanıldıklarında daha sonraki bir noktada çağrılabilirler.

Aslında, tanımlamak yerine

def callByName(x: => Int) = {
  println("x1=" + x)
  println("x2=" + x)
}

ve çalışıyor:

scala> callByName(something())
calling something
x1=1
calling something
x2=1

Ayrıca şunu da yazabilirsiniz:

def callAlsoByName(x: () => Int) = {
  println("x1=" + x())
  println("x2=" + x())
}

Ve aynı etki için aşağıdaki gibi çalıştırın:

callAlsoByName(() => {something()})

calling something
x1=1
calling something
x2=1

Sanırım demek istediniz: <! - dil: lang-scala -> def callAlsoByName (x: () => Int) = {println ("x1 =" + x ()) println ("x2 =" + x ( ))} ve sonra: <! - dil: lang-js -> callAlsoByName (() => bir şey ()) Bu son çağrıda bir şey () etrafında süslü parantezlere ihtiyacınız olduğunu düşünmüyorum. Not: Sadece cevabınızı düzenlemeye çalıştım, ancak düzenlemem bunun yorum veya ayrı bir yanıt olması gerektiğini söyleyen yorumcular tarafından reddedildi.
lambdista

Görünüşe göre yorumlarda sözdizimi vurgulamayı kullanamazsınız , bu yüzden "<! - dil: lang-scala ->" bölümünü dikkate almayın! Kendi yorumumu düzenlerdim, ancak sadece 5 dakika içinde yapmanıza izin verilir! :)
lambdista

1
Son zamanlarda da bununla karşılaştım. Kavramsal olarak bu şekilde düşünmek iyidir, ancak skala => Tve arasında ayrım yapar () => T. İlk türü parametre olarak alan bir işlev ikinciyi kabul etmez, scala @ScalaSignatureek açıklamada bunun için bir derleme zamanı hatası atmak için yeterli bilgi depolar . Her ikisi için bayt kodu => Tve () => Taynı olsa ve bir Function0. Daha fazla ayrıntı için bu soruya bakın.
vsnyc

6

Sadece bir örnek vermek yerine basit bir kullanım örneğiyle açıklamaya çalışacağım

Siz nagged son kez bu yana Nag her zaman bir "nagger uygulaması" oluşturmak istediğiniz düşünün .

Aşağıdaki uygulamaları inceleyin:

object main  {

    def main(args: Array[String]) {

        def onTime(time: Long) {
            while(time != time) println("Time to Nag!")
            println("no nags for you!")
        }

        def onRealtime(time: => Long) {
            while(time != time) println("Realtime Nagging executed!")
        }

        onTime(System.nanoTime())
        onRealtime(System.nanoTime())
    }
}

Yukarıdaki uygulamada, nagger sadece isimle geçerken çalışacaktır, bunun nedeni, değere göre geçerken yeniden kullanılacağı ve bu nedenle değerin yeniden değerlendirilmeyeceği, değer isimle geçerken her değişkenlere erişildiği zaman


4

Tipik olarak, fonksiyonların parametreleri by-value parametreleridir; yani, parametrenin değeri işleve geçirilmeden önce belirlenir. Ancak, parametre olarak kabul eden bir işlevi, fonksiyonumuz içinde çağrılıncaya kadar değerlendirilmesini istemediğimiz bir ifade yazmamız gerekirse ne olur? Bu durumda, Scala isimle çağrı parametreleri sunar.

İsme göre çağrı mekanizması, kodlama koduna bir kod bloğunu iletir ve callee parametreye her eriştiğinde, kod bloğu yürütülür ve değer hesaplanır.

object Test {
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)
  t
}
}
 1. C: /> scalac Test.scala 
 2. scala Testi
 3. Gecikmeli yöntemde
 4. Nano saniyede zaman kazanma
 5. Param: 81303808765843
 6. Nano saniyede zaman kazanma

2

Tahmin ettiğim gibi, call-by-valueyukarıda tartışıldığı gibi işlev sadece değerleri işleve iletir. Buna göre Martin Odersky, fonksiyon değerlendirmesinde önemli rol oynayan bir Scala tarafından takip edilen bir Değerlendirme stratejisidir. Ama basitleştirin call-by-name. onun gibi bir yöntem olarak bir argüman olarak geçmek işlevini de bilmek Higher-Order-Functions. Yöntem, iletilen parametrenin değerine eriştiğinde, iletilen işlevlerin uygulanmasını çağırır. aşağıda olduğu gibi:

@Dhg örneğine göre, önce yöntemi şu şekilde oluşturun:

def something() = {
 println("calling something")
 1 // return value
}  

Bu işlev bir printlnifade içerir ve bir tamsayı değeri döndürür. Aşağıdaki gibi bağımsız değişkenleri olan işlevi oluşturun call-by-name:

def callByName(x: => Int) = {
 println("x1=" + x)
 println("x2=" + x)
}

Bu işlev parametresi, bir tamsayı değeri döndüren anonim bir işlev tanımlar. Burada argümanlar geçen ancak geri dönüş değeri xolan fonksiyon tanımını ve fonksiyonumuz aynı imzayı içerir. İşlevi çağırdığımızda, işlevi bağımsız değişken olarak geçiririz . Ancak , sadece tamsayı değerini işleve iletir. İşlevi aşağıdaki gibi çağırıyoruz:0intsomethingcallByNamecall-by-value

scala> callByName(something())
 calling something
 x1=1
 calling something
 x2=1 

Bu somethingyöntemde iki kez denir, çünkü xin callByNamemetodunun değerine eriştiğimizde, metodun tanımına çağrılır something.


2

Değere göre çağrı, burada birçok cevapla açıklandığı gibi genel kullanım durumudur.

İsimle çağrı , arayana bir kod bloğu geçirir ve arayan parametreye her eriştiğinde, kod bloğu yürütülür ve değer hesaplanır.

Aşağıdaki kullanım örnekleriyle aramayı adıyla daha basit bir şekilde göstermeye çalışacağım

Örnek 1:

İsme göre basit örnek / kullanım durumu işlevi parametre olarak alan ve geçen süreyi veren fonksiyonun altındadır.

 /**
   * Executes some code block and prints to stdout the 
time taken to execute   the block 
for interactive testing and debugging.
   */
  def time[T](f: => T): T = {
    val start = System.nanoTime()
    val ret = f
    val end = System.nanoTime()

    println(s"Time taken: ${(end - start) / 1000 / 1000} ms")

    ret
  }

Örnek 2:

(Scala) ile apache'nin kıvılcım bakınız adı arada çağrısı kullanarak oturum kullanır Loggingözellik içinde kendi lazily değerlendirir olup log.isInfoEnabledya da olmayan bir yöntem, aşağıdaki.

protected def logInfo(msg: => String) {
     if (log.isInfoEnabled) log.info(msg)
 }

2

Bir de değer tarafından çağrı , ifade değeri işlev çağrısının zamanda önceden hesaplanabilmekte ve özel bir değer mukabil işleve parametre olarak geçirilir olup. Tüm fonksiyon boyunca aynı değer kullanılacaktır.

Oysa Ad ile Çağrı'da , ifadenin kendisi işleve bir parametre olarak iletilir ve yalnızca belirli bir parametre çağrıldığında işlevin içinde hesaplanır.

Scala'da Call by Name ve Value by Call arasındaki fark aşağıdaki örnekle daha iyi anlaşılabilir:

Kod Parçacığı

object CallbyExample extends App {

  // function definition of call by value
  def CallbyValue(x: Long): Unit = {
    println("The current system time via CBV: " + x);
    println("The current system time via CBV " + x);
  }

  // function definition of call by name
  def CallbyName(x: => Long): Unit = {
    println("The current system time via CBN: " + x);
    println("The current system time via CBN: " + x);
  }

  // function call
  CallbyValue(System.nanoTime());
  println("\n")
  CallbyName(System.nanoTime());
}

Çıktı

The current system time via CBV: 1153969332591521
The current system time via CBV 1153969332591521


The current system time via CBN: 1153969336749571
The current system time via CBN: 1153969336856589

Yukarıdaki kod snippet'inde, CallbyValue (System.nanoTime ()) işlev çağrısı için, sistem nano süresi önceden hesaplanır ve bu önceden hesaplanan değer, işlev çağrısına bir parametre iletilir.

Ancak CallbyName (System.nanoTime ()) işlev çağrısında, "System.nanoTime ())" ifadesi, işlev çağrısına bir parametre olarak iletilir ve bu ifade işlev içinde kullanıldığında, bu ifadenin değeri hesaplanır .

X parametresini ve veri tipini ayıran bir => sembolü bulunan CallbyName işlevinin işlev tanımına dikkat edin . Bu özel sembol, işlevin ad türüne göre çağrıldığını gösterir.

Diğer bir deyişle, değere göre çağrı işlevi bağımsız değişkenleri işleve girmeden önce bir kez değerlendirilir, ancak ada göre çağrı işlevi bağımsız değişkenleri yalnızca ihtiyaç duyulduğunda işlevin içinde değerlendirilir.

Bu yardımcı olur umarım!


2

İşte şu anda Scala kursuna devam eden bir meslektaşıma yardım etmek için kodladığım kısa bir örnek. İlginç olduğunu düşündüğüm, Martin'in derste daha önce verilen && soru yanıtını örnek olarak kullanmamasıydı. Her durumda umarım bu yardımcı olur.

val start = Instant.now().toEpochMilli

val calc = (x: Boolean) => {
    Thread.sleep(3000)
    x
}


def callByValue(x: Boolean, y: Boolean): Boolean = {
    if (!x) x else y
}

def callByName(x: Boolean, y: => Boolean): Boolean = {
    if (!x) x else y
}

new Thread(() => {
    println("========================")
    println("Call by Value " + callByValue(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


new Thread(() => {
    println("========================")
    println("Call by Name " + callByName(false, calc(true)))
    println("Time " + (Instant.now().toEpochMilli - start) + "ms")
    println("========================")
}).start()


Thread.sleep(5000)

Kodun çıktısı aşağıdaki gibi olacaktır:

========================
Call by Name false
Time 64ms
========================
Call by Value false
Time 3068ms
========================

1

Parametreler genellikle değere göre geçer, yani işlev gövdesinde değiştirilmeden önce değerlendirilirler.

İşlevi tanımlarken çift oku kullanarak bir parametreyi ada göre çağırmaya zorlayabilirsiniz.

// first parameter will be call by value, second call by name, using `=>`
def returnOne(x: Int, y: => Int): Int = 1

// to demonstrate the benefits of call by name, create an infinite recursion
def loop(x: Int): Int = loop(x)

// will return one, since `loop(2)` is passed by name so no evaluated
returnOne(2, loop(2))

// will not terminate, since loop(2) will evaluate. 
returnOne(loop(2), 2) // -> returnOne(loop(2), 2) -> returnOne(loop(2), 2) -> ... 

1

İ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*24

Ö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 * 749
call-by-name: (3+4)*(3+4)7 * (3+4)7 * 749

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 * 749
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 * 749
call-by-name: (3+4)*(3+4)7*(3+4)7*749

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
}

1

Bir örnek incelemek farkı daha iyi anlamanıza yardımcı olacaktır.

Geçerli saati döndüren basit bir işlevi tanımlayalım:

def getTime = System.currentTimeMillis

Şimdi, adıyla , saniyede iki kez geciktirilen bir işlev tanımlayacağız :

def getTimeByName(f: => Long) = { println(f); Thread.sleep(1000); println(f)}

Ve değere göre bir :

def getTimeByValue(f: Long) = { println(f); Thread.sleep(1000); println(f)}

Şimdi her birini arayalım:

getTimeByName(getTime)
// prints:
// 1514451008323
// 1514451009325

getTimeByValue(getTime)
// prints:
// 1514451024846
// 1514451024846

Sonuç farkı açıklamalıdır. Parçacığı burada bulabilirsiniz .


0

CallByNamekullanıldığında callByValueçağrılır ve ifadeyle her karşılaşıldığında çağrılır.

Örneğin:-

Sonsuz bir döngü var yani bu işlevi yürütürseniz biz asla scalaistem alacak .

scala> def loop(x:Int) :Int = loop(x-1)
loop: (x: Int)Int

bir callByNamefonksiyon yukarıdaki loopmetodu argüman olarak alır ve asla kendi bedeninde kullanılmaz.

scala> def callByName(x:Int,y: => Int)=x
callByName: (x: Int, y: => Int)Int

callByNameYöntemin yürütülmesinde herhangi bir sorun bulamıyoruz ( scalaistemi geri alıyoruz), çünkü fonksiyonun içindeki loop fonksiyonunu hiçbir yerde kullanmıyoruz callByName.

scala> callByName(1,loop(10))
res1: Int = 1
scala> 

bir callByValuefonksiyon yukarıdaki loopmetodu parametre olarak alır, sonuç olarak fonksiyon veya ifade, dış fonksiyonu looptekrar tekrar yürütülen fonksiyon tarafından yürütülmeden önce değerlendirilir ve asla scalageri istemeziz.

scala> def callByValue(x:Int,y:Int) = x
callByValue: (x: Int, y: Int)Int

scala> callByValue(1,loop(1))

0

Bunu gör:

    object NameVsVal extends App {

  def mul(x: Int, y: => Int) : Int = {
    println("mul")
    x * y
  }
  def add(x: Int, y: Int): Int = {
    println("add")
    x + y
  }
  println(mul(3, add(2, 1)))
}

y: => Int, ada göre çağrıdır. İsme göre çağrı olarak iletilen ekleme (2, 1) 'dir. Bu tembel bir şekilde değerlendirilecektir. Konsoldaki çıktı önce "mul" ve ardından "add" olacak, ancak add ilk olarak çağrılmış gibi görünecektir. Ada göre arama, bir işlev işaretçisini geçirme gibi davranır.
Şimdi y: => Int yerine y: Int olarak değiştirin. Konsol "ekle" ve ardından "mul" gösterecektir! Genel değerlendirme yöntemi.


-2

Buradaki tüm cevapların doğru gerekçeyi yaptığını düşünmüyorum:

Değere göre çağrıda, argümanlar yalnızca bir kez hesaplanır:

def f(x : Int, y :Int) = x

// following the substitution model

f(12 + 3, 4 * 11)
f(15, 4194304)
15

yukarıda tüm argümanların gerekli olup olmadığının değerlendirildiğini, normalde call-by-valuehızlı olabileceğini , ancak her zaman olduğu gibi olmadığını görebilirsiniz.

Değerlendirme stratejisi call-by-nameolsaydı, ayrışma şu olurdu:

f(12 + 3, 4 * 11)
12 + 3
15

Yukarıda da görebileceğiniz gibi, değerlendirmek için hiçbir zaman ihtiyaç duymadık 4 * 11ve dolayısıyla bazen faydalı olabilecek biraz hesaplama kaydetmedik .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.