Chiamata per nome vs chiamata per valore in Scala, chiarimento necessario


239

A quanto ho capito, in Scala una delle due funzioni può essere chiamata

  • per valore o
  • per nome

Ad esempio, date le seguenti dichiarazioni, sappiamo come verrà chiamata la funzione?

Dichiarazione:

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

Chiamata

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

Quali sono le regole per favore?

Risposte:


540

L'esempio che hai fornito utilizza solo call-by-value, quindi fornirò un nuovo esempio più semplice che mostra la differenza.

Innanzitutto, supponiamo di avere una funzione con un effetto collaterale. Questa funzione stampa qualcosa e quindi restituisce un Int.

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

Ora definiremo due funzioni che accettano Intargomenti esattamente uguali, tranne per il fatto che uno accetta l'argomento in uno stile call-by-value ( x: Int) e l'altro in uno stile call-by-name ( x: => Int).

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

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

Cosa succede quando li chiamiamo con la nostra funzione di effetto collaterale?

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

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

Quindi puoi vedere che nella versione call-by-value, l'effetto collaterale della funzione call-in passata ( something()) si è verificato solo una volta. Tuttavia, nella versione call-by-name, l'effetto collaterale si è verificato due volte.

Questo perché le funzioni call-by-value calcolano il valore dell'espressione passata prima di chiamare la funzione, quindi si accede sempre allo stesso valore. Invece, le funzioni di chiamata per nome ricalcolano il valore dell'espressione passata ogni volta che si accede.


296
Ho sempre pensato che questa terminologia sia inutilmente confusa. Una funzione può avere più parametri che variano nel loro stato di chiamata per nome vs chiamata per valore. Quindi non è che una funzione sia chiamata per nome o chiamata per valore, è che ciascuno dei suoi parametri può essere pass -by-name o pass-by-value. Inoltre, "call-by-name" non ha nulla a che fare con i nomi . => Intè un tipo diverso da Int; è "funzione di nessun argomento che genererà un Int" vs solo Int. Una volta che hai funzioni di prima classe non è necessario inventare una terminologia chiamata per nome per descriverlo.
Ben

2
@Ben, questo aiuta a rispondere a un paio di domande, grazie. Vorrei che altri scritti spiegassero chiaramente la semantica del pass-by-name.
Christopher Poile,

3
@SelimOber Se il testo f(2)viene compilato come espressione di tipo Int, il codice generato chiama fcon argomento 2e il risultato è il valore dell'espressione. Se lo stesso testo viene compilato come espressione di tipo, => Intil codice generato utilizza un riferimento a una sorta di "blocco di codice" come valore dell'espressione. In entrambi i casi, un valore di quel tipo può essere passato a una funzione in attesa di un parametro di quel tipo. Sono abbastanza sicuro che puoi farlo con un'assegnazione variabile, senza che nessun parametro passi in vista. Quindi, cosa hanno a che fare i nomi o le chiamate?
Ben

4
@Ben Quindi, se => Intè "funzione di nessun argomento che genera un Int", come è diverso da quello () => Int? Scala sembra trattarli in modo diverso, ad esempio => Intapparentemente non funziona come il tipo di a val, solo come il tipo di un parametro.
Tim Goodman,

5
@TimGoodman Hai ragione, è un po 'più complicato di quanto ho capito. => Intè una comodità e non è implementato esattamente come un oggetto funzione (presumibilmente perché non si possono avere variabili di tipo => Int, sebbene non ci sia una ragione fondamentale per cui questo non possa funzionare). () => Intè esplicitamente una funzione di nessun argomento che restituirà un Int, che deve essere chiamato esplicitamente e può essere passato come una funzione. => Intè una specie di "proxy Int", e l'unica cosa che puoi fare è chiamarlo (implicitamente) per ottenere il file Int.
Ben

51

Ecco un esempio di Martin Odersky:

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

Vogliamo esaminare la strategia di valutazione e determinare quale è più veloce (meno passaggi) in queste condizioni:

test (2,3)

chiama per valore: test (2,3) -> 2 * 2 -> 4
chiama per nome: test (2,3) -> 2 * 2 -> 4
Qui il risultato è raggiunto con lo stesso numero di passaggi.

test (3+4,8)

chiama per valore: test (7,8) -> 7 * 7 -> 49
chiama per nome: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Qui chiama per valore è più veloce.

test (7,2*4)

chiama per valore: test (7,8) -> 7 * 7 -> 49
chiama per nome: 7 * 7 -> 49
Qui chiama per nome è più veloce

test (3+4, 2*4) 

chiama per valore: test (7,2 * 4) -> test (7, 8) -> 7 * 7 -> 49
chiama per nome: (3 + 4) (3 + 4) -> 7 (3 + 4) -> 7 * 7 -> 49
Il risultato viene raggiunto con gli stessi passaggi.


1
Nel terzo esempio per CBV, penso che volevi dire test (7,8) anziché test (7,14)
talonx

1
L'esempio è tratto da Coursera, principio nella programmazione della scala. Conferenza 1.2. La chiamata per nome dovrebbe leggere def test (x:Int, y: => Int) = x * xche il parametro y non viene mai utilizzato.
Dr Jerry,

1
Buon esempio! Tratto dal MOOC di Coursera :)
alxsimo,

Questa è una buona spiegazione della differenza, ma non si rivolge alla domanda posta, vale a dire quale dei due sta chiamando Scala
db1234

16

Nel caso del tuo esempio, tutti i parametri verranno valutati prima di essere richiamati nella funzione, poiché li definisci solo in base al valore . Se si desidera definire i parametri per nome, è necessario passare un blocco di codice:

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

In questo modo il parametro xnon verrà valutato fino a quando non viene chiamato nella funzione.

Questo piccolo post qui spiega bene anche questo.


10

Per iterare il punto di @ Ben nei commenti sopra, penso che sia meglio pensare a "call-by-name" come solo zucchero sintattico. Il parser avvolge le espressioni in funzioni anonime, in modo che possano essere richiamate in un secondo momento, quando vengono utilizzate.

In effetti, invece di definire

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

e in esecuzione:

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

Puoi anche scrivere:

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

Ed eseguilo come segue per lo stesso effetto:

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

calling something
x1=1
calling something
x2=1

Penso che volevi dire: <! - lingua: lang-scala -> def callAlsoByName (x: () => Int) = {println ("x1 =" + x ()) println ("x2 =" + x ( ))} e poi: <! - language: lang-js -> callAlsoByName (() => qualcosa ()) Non credo che ti servano le parentesi graffe intorno a qualcosa () in quest'ultima chiamata. Nota: ho provato a modificare la tua risposta, ma la mia modifica è stata rifiutata dai revisori dicendo che dovrebbe essere invece un commento o una risposta separata.
lambdista,

Apparentemente non puoi usare l' evidenziazione della sintassi nei commenti, quindi ignora la parte "<! - language: lang-scala ->"! Avrei modificato il mio commento ma ti è permesso farlo solo entro 5 minuti! :)
lambdista

1
Di recente mi sono imbattuto anche in questo. Va bene pensare concettualmente in questo modo, ma scala differenzia tra => Te () => T. Una funzione che accetta il primo tipo come parametro, non accetterà il secondo, scala memorizza abbastanza informazioni nell'annotazione @ScalaSignatureper generare un errore di tempo di compilazione per questo. Il bytecode per entrambi => Ted () => Tè lo stesso però ed è a Function0. Vedi questa domanda per maggiori dettagli.
vsnyc,

6

Proverò a spiegare con un semplice caso d'uso piuttosto che semplicemente fornendo un esempio

Immagina di voler creare un'app "nagger" che ti farà venire il tormento ogni volta dall'ultima volta che ti sei assillato.

Esamina le seguenti implementazioni:

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())
    }
}

Nell'implementazione di cui sopra il nagger funzionerà solo quando si passa per nome il motivo è che, quando si passa per valore, verrà riutilizzato e quindi il valore non verrà rivalutato mentre quando si passa per nome il valore verrà rivalutato ogni volta che si accede alle variabili


4

In genere, i parametri delle funzioni sono parametri per valore; cioè, il valore del parametro viene determinato prima che venga passato alla funzione. E se avessimo bisogno di scrivere una funzione che accetta come parametro un'espressione che non vogliamo valutare fino a quando non viene chiamata all'interno della nostra funzione? Per questa circostanza, Scala offre parametri di chiamata per nome.

Un meccanismo di chiamata per nome passa un blocco di codice alla chiamata e ogni volta che la chiamata accede al parametro, il blocco di codice viene eseguito e il valore viene calcolato.

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 test
 3. In metodo ritardato
 4. Ottenere tempo in nano secondi
 5. Param: 81303808765843
 6. Ottenere tempo in nano secondi

2

Come presumo, la call-by-valuefunzione come discusso sopra passa solo i valori alla funzione. Secondo Martin OderskyIt è una strategia di valutazione seguita da una Scala che gioca un ruolo importante nella valutazione delle funzioni. Ma rendilo semplice call-by-name. è come passare la funzione come argomento al metodo noto anche come Higher-Order-Functions. Quando il metodo accede al valore del parametro passato, chiama l'implementazione delle funzioni passate. come sotto:

Secondo l'esempio @dhg, creare prima il metodo come:

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

Questa funzione contiene printlnun'istruzione e restituisce un valore intero. Crea la funzione, che ha argomenti come call-by-name:

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

Questo parametro di funzione, è definire una funzione anonima che ha restituito un valore intero. In questo xcontiene una definizione di funzione che ha 0passato argomenti ma restituisce intvalore e la nostra somethingfunzione contiene la stessa firma. Quando chiamiamo la funzione, passiamo la funzione come argomento a callByName. Ma nel caso in call-by-valuecui passi solo il valore intero alla funzione. Chiamiamo la funzione come di seguito:

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

In questo il nostro somethingmetodo ha chiamato due volte, perché quando accediamo al valore di xin callByNamemethod, la sua chiamata alla definizione di somethingmethod.


2

La chiamata per valore è un caso d'uso generale, come spiegato da molte risposte qui.

Chiamata per nome passa un blocco di codice al chiamante e ogni volta che il chiamante accede al parametro, il blocco di codice viene eseguito e il valore viene calcolato.

Proverò a dimostrare il modo più semplice di chiamare per nome con i casi d'uso di seguito

Esempio 1:

Il semplice esempio / caso d'uso della chiamata per nome è sotto la funzione, che assume la funzione come parametro e fornisce il tempo trascorso.

 /**
   * 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
  }

Esempio 2:

apache spark (con scala) usa la registrazione usando la chiamata per nome in modo da vedere Loggingtratto in cui il suo pigramente valuta se log.isInfoEnableddal metodo seguente.

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

2

In una chiamata per valore , il valore dell'espressione viene pre-calcolato al momento della chiamata della funzione e quel particolare valore viene passato come parametro alla funzione corrispondente. Lo stesso valore verrà utilizzato per tutta la funzione.

Mentre in una chiamata per nome , l'espressione stessa viene passata come parametro alla funzione e viene calcolata solo all'interno della funzione, ogni volta che viene chiamato quel particolare parametro.

La differenza tra Call by Name e Call by Value in Scala potrebbe essere meglio compresa con l'esempio seguente:

Snippet di codice

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

Produzione

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

Nel frammento di codice sopra riportato, per la funzione call CallbyValue (System.nanoTime ()) , il tempo nano di sistema viene pre-calcolato e quel valore pre-calcolato è stato passato un parametro alla chiamata di funzione.

Ma nella chiamata della funzione CallbyName (System.nanoTime ()) , l'espressione "System.nanoTime ())" stessa viene passata come parametro alla chiamata della funzione e il valore di tale espressione viene calcolato quando quel parametro viene utilizzato all'interno della funzione .

Notare la definizione della funzione della funzione CallbyName, in cui è presente un simbolo => che separa il parametro x e il suo tipo di dati. Quel particolare simbolo indica che la funzione è di tipo chiamata per nome.

In altre parole, gli argomenti della funzione call by value vengono valutati una volta prima di entrare nella funzione, ma gli argomenti della funzione call by name vengono valutati all'interno della funzione solo quando sono necessari.

Spero che questo ti aiuti!


2

Ecco un breve esempio che ho codificato per aiutare un mio collega che sta attualmente frequentando il corso alla Scala. Quello che pensavo fosse interessante è che Martin non ha usato la risposta alla domanda && presentata precedentemente nella lezione come esempio. In ogni caso spero che questo aiuti.

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)

L'output del codice sarà il seguente:

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

1

I parametri vengono generalmente passati per valore, il che significa che verranno valutati prima di essere sostituiti nel corpo della funzione.

È possibile forzare la chiamata di un parametro per nome utilizzando la doppia freccia durante la definizione della funzione.

// 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

Ci sono già molte risposte fantastiche per questa domanda in Internet. Scriverò una raccolta di diverse spiegazioni ed esempi che ho raccolto sull'argomento, nel caso qualcuno lo trovasse utile

INTRODUZIONE

call-by-value (CBV)

In genere, i parametri delle funzioni sono parametri di chiamata per valore; vale a dire, i parametri vengono valutati da sinistra a destra per determinarne il valore prima che venga valutata la funzione stessa

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

call-by-name (CBN)

E se avessimo bisogno di scrivere una funzione che accetta come parametro un'espressione che non dobbiamo valutare fino a quando non viene chiamata all'interno della nostra funzione? Per questa circostanza, Scala offre parametri di chiamata per nome. Significa che il parametro viene passato nella funzione così com'è e la sua valutazione avviene dopo la sostituzione

def first1(a: Int, b: => Int): Int = a
first1(3 + 4, 5 + 6) // will be reduced to (3 + 4) and then to 7

Un meccanismo di chiamata per nome passa un blocco di codice alla chiamata e ogni volta che la chiamata accede al parametro, il blocco di codice viene eseguito e il valore viene calcolato. Nell'esempio seguente, ritardato stampa un messaggio che dimostra che il metodo è stato inserito. Successivamente, in ritardo stampa un messaggio con il suo valore. Infine, i rendimenti ritardati 't':

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

In metodo ritardato
Ottenimento del tempo in nano secondi
Param: 2027245119786400

PRO E CONTRO PER OGNI CASO

CBN: + Termina più spesso * controlla sotto la terminazione sopra * + Ha il vantaggio che un argomento di funzione non viene valutato se il parametro corrispondente non viene utilizzato nella valutazione del corpo della funzione -È più lento, crea più classi (il che significa che il programma accetta più lungo da caricare) e consuma più memoria.

CBV: + È spesso esponenzialmente più efficiente di CBN, perché evita questo ripetuto ricalcolo degli argomenti che le espressioni chiamate per nome comportano. Valuta ogni argomento della funzione solo una volta + Gioca molto meglio con effetti imperativi ed effetti collaterali, perché tendi a conoscere molto meglio quando verranno valutate le espressioni. -Può portare ad un ciclo durante la valutazione dei suoi parametri * controllare sotto la terminazione sopra *

Cosa succede se la risoluzione non è garantita?

-Se la valutazione CBV di un'espressione e termina, allora termina anche la valutazione CBN di e -L'altra direzione non è vera

Esempio di non terminazione

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

Considera prima l'espressione (1, loop)

CBN: primo (1, ciclo) → 1 CBV: primo (1, ciclo) → riduce gli argomenti di questa espressione. Poiché uno è un ciclo, riduce gli argomenti in modo infinito. Non termina

DIFFERENZE IN OGNI COMPORTAMENTO DEL CASO

Definiamo un metodo di prova che sarà

Def test(x:Int, y:Int) = x * x  //for call-by-value
Def test(x: => Int, y: => Int) = x * x  //for call-by-name

Test Case1 (2,3)

test(2,3)2*24

Poiché iniziamo con argomenti già valutati, sarà lo stesso numero di passaggi per chiamata per valore e chiamata per nome

Test Case2 (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

In questo caso, call-by-value esegue meno passaggi

Test Case3 (7, 2 * 4)

call-by-value: test(7, 2*4) → test(7,8)7 * 749
call-by-name: (7)*(7)49

Evitiamo il calcolo non necessario del secondo argomento

Test Case4 (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

Approccio diverso

Innanzitutto, supponiamo di avere una funzione con un effetto collaterale. Questa funzione stampa qualcosa e quindi restituisce un Int.

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

Ora definiremo due funzioni che accettano argomenti Int esattamente identici, tranne per il fatto che uno accetta l'argomento in uno stile call-by-value (x: Int) e l'altro in uno stile call-by-name (x: => Int).

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

Cosa succede quando li chiamiamo con la nostra funzione di effetto collaterale?

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

Quindi puoi vedere che nella versione call-by-value, l'effetto collaterale della chiamata di funzione passata (qualcosa ()) si è verificato solo una volta. Tuttavia, nella versione call-by-name, l'effetto collaterale si è verificato due volte.

Questo perché le funzioni call-by-value calcolano il valore dell'espressione passata prima di chiamare la funzione, quindi si accede sempre allo stesso valore. Tuttavia, le funzioni di chiamata per nome ricalcolano il valore dell'espressione passata ogni volta che si accede.

ESEMPI DOVE È MEGLIO UTILIZZARE CHIAMATA PER NOME

Da: https://stackoverflow.com/a/19036068/1773841

Semplice esempio di prestazioni: registrazione.

Immaginiamo un'interfaccia come questa:

trait Logger {
  def info(msg: => String)
  def warn(msg: => String)
  def error(msg: => String)
}

E poi usato così:

logger.info("Time spent on X: " + computeTimeSpent)

Se il metodo info non fa nulla (perché, diciamo, il livello di registrazione è stato configurato per un livello superiore a quello), allora computeTimeSpent non viene mai chiamato, risparmiando tempo. Questo succede molto con i logger, dove spesso si vede la manipolazione di stringhe che può essere costosa rispetto alle attività che vengono registrate.

Esempio di correttezza: operatori logici.

Probabilmente hai visto questo codice:

if (ref != null && ref.isSomething)

Immagina di dichiarare il metodo && in questo modo:

trait Boolean {
  def &&(other: Boolean): Boolean
}

quindi, ogni volta che ref è null, verrà visualizzato un errore poiché isSomething verrà chiamato su un riferimento null prima di essere passato a &&. Per questo motivo, la dichiarazione effettiva è:

trait Boolean {
  def &&(other: => Boolean): Boolean =
    if (this) this else other
}

1

Fare un esempio dovrebbe aiutarti a capire meglio la differenza.

Definiamo una semplice funzione che restituisce l'ora corrente:

def getTime = System.currentTimeMillis

Ora definiremo una funzione, per nome , che viene stampata due volte in ritardo di un secondo:

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

E uno per valore :

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

Ora chiamiamo ciascuno:

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

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

Il risultato dovrebbe spiegare la differenza. Lo snippet è disponibile qui .


0

CallByNameviene richiamato quando utilizzato e callByValueviene richiamato ogni volta che viene rilevata l'istruzione.

Per esempio:-

Ho un ciclo infinito, cioè se esegui questa funzione non saremo mai scalapronti.

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

una callByNamefunzione prende sopra il loopmetodo come argomento e non viene mai usata all'interno del suo corpo.

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

Sull'esecuzione del callByNamemetodo non troviamo alcun problema (riceviamo il scalaprompt indietro) poiché non siamo in grado di utilizzare la funzione loop all'interno della callByNamefunzione.

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

una callByValuefunzione prende il loopmetodo sopra come parametro come risultato che viene valutata all'interno della funzione o dell'espressione prima di eseguire la funzione esterna lì dalla loopfunzione eseguita ricorsivamente e non riceviamo mai scalarisposta.

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

scala> callByValue(1,loop(1))

0

Guarda questo:

    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 è chiamata per nome. Ciò che viene passato come chiamata per nome è add (2, 1). Questo sarà valutato pigramente. Quindi l'output su console sarà "mul" seguito da "add", anche se add sembra essere chiamato per primo. La chiamata per nome funge da tipo di passaggio del puntatore a funzione.
Ora cambia da y: => Int a y: Int. La console mostrerà "aggiungi" seguito da "mul"! Modo usuale di valutazione.


-2

Non penso che tutte le risposte qui abbiano la giustificazione corretta:

Nella chiamata per valore gli argomenti vengono calcolati una sola volta:

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

// following the substitution model

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

puoi vedere sopra che tutti gli argomenti vengono valutati se non sono necessari, normalmente call-by-valuepossono essere veloci ma non sempre come in questo caso.

Se la strategia di valutazione fosse stata call-by-namequindi la decomposizione sarebbe stata:

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

come puoi vedere sopra, non abbiamo mai avuto bisogno di valutare 4 * 11e quindi abbiamo risparmiato un po 'di calcolo che a volte può essere utile.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.