Qual è la differenza tra:
def even: Int => Boolean = _ % 2 == 0
e
val even: Int => Boolean = _ % 2 == 0
Entrambi possono essere chiamati come even(10).
Qual è la differenza tra:
def even: Int => Boolean = _ % 2 == 0
e
val even: Int => Boolean = _ % 2 == 0
Entrambi possono essere chiamati come even(10).
Risposte:
Il metodo def evenvaluta alla chiamata e crea ogni volta una nuova funzione (nuova istanza di Function1).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Con defte puoi ottenere una nuova funzione ad ogni chiamata:
val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1049057402
test()
// Int = -1049057402 - same result
def test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -240885810
test()
// Int = -1002157461 - new result
valvaluta quando definito, def- quando chiamato:
scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing
scala> def even: Int => Boolean = ???
even: Int => Boolean
scala> even
scala.NotImplementedError: an implementation is missing
Si noti che c'è una terza opzione: lazy val.
Valuta quando viene chiamato la prima volta:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Restituisce sempre lo stesso risultato (in questo caso la stessa istanza di FunctionN) ogni volta:
lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
lazy val test: () => Int = {
val r = util.Random.nextInt
() => r
}
test()
// Int = -1068569869
test()
// Int = -1068569869 - same result
Prestazione
val valuta quando definito.
defvaluta su ogni chiamata, quindi le prestazioni potrebbero essere peggiori rispetto vala più chiamate. Otterrai le stesse prestazioni con una sola chiamata. E senza chiamate non avrai costi generali def, quindi puoi definirlo anche se non lo userai in alcuni rami.
Con a lazy valotterrai una valutazione pigra: puoi definirla anche se non la userai in alcuni rami, e la valutazione una volta o mai più, ma otterrai un piccolo overhead dal doppio controllo del blocco su ogni accesso al tuo lazy val.
Come notato da @SargeBorsch, potresti definire il metodo, e questa è l'opzione più veloce:
def even(i: Int): Boolean = i % 2 == 0
Ma se hai bisogno di una funzione (non metodo) per la composizione della funzione o per funzioni di ordine superiore (come filter(even)) il compilatore genererà una funzione dal tuo metodo ogni volta che la usi come funzione, quindi le prestazioni potrebbero essere leggermente peggiori rispetto a val.
evenviene chiamato.
defpuò essere usato per definire un metodo, e questa è l'opzione più veloce. @ A.Karimi
even eq even.
@inlineattributo per questo. Ma non può incorporare le funzioni perché la chiamata di funzione è una chiamata al applymetodo virtuale di un oggetto funzione. JVM potrebbe devirtualizzare e incorporare tali chiamate in alcune situazioni, ma non in generale.
Considera questo:
scala> def even: (Int => Boolean) = {
println("def");
(x => x % 2 == 0)
}
even: Int => Boolean
scala> val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
val //gets printed while declaration. line-4
even2: Int => Boolean = <function1>
scala> even(1)
def
res9: Boolean = false
scala> even2(1)
res10: Boolean = false
Vedi la differenza? In breve:
def : per ogni chiamata a even, chiama evennuovamente il corpo del metodo. Ma con even2ie val , la funzione viene inizializzata solo una volta durante la dichiarazione (e quindi stampa valalla riga 4 e mai più) e lo stesso output viene utilizzato ogni volta che accede. Ad esempio, prova a fare questo:
scala> import scala.util.Random
import scala.util.Random
scala> val x = { Random.nextInt }
x: Int = -1307706866
scala> x
res0: Int = -1307706866
scala> x
res1: Int = -1307706866
Quando xviene inizializzato, il valore restituito da Random.nextIntviene impostato come valore finale di x. La prossima volta che xviene usato di nuovo, restituirà sempre lo stesso valore.
Puoi anche inizializzare pigramente x. cioè la prima volta che viene usato viene inizializzato e non durante la dichiarazione. Per esempio:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2due volte, una volta con 1e una volta con 2. Riceverai risposte diverse in ogni chiamata. Quindi, mentre printlnnon viene eseguito nelle chiamate successive, non si ottiene lo stesso risultato da chiamate diverse a even2. Per quanto riguarda il motivo per cui printlnnon viene eseguito di nuovo, questa è una domanda diversa.
Guarda questo:
var x = 2 // using var as I need to change it to 3 later
val sq = x*x // evaluates right now
x = 3 // no effect! sq is already evaluated
println(sq)
Sorprendentemente, questo stamperà 4 e non 9! val (anche var) viene valutato immediatamente e assegnato.
Ora cambia val in def .. stamperà 9! Def è una chiamata di funzione .. verrà valutata ogni volta che viene chiamata.
val ie "sq" è da Scala la definizione è fissa. Viene valutato proprio al momento della dichiarazione, non è possibile modificarlo in seguito. In altri esempi, in cui even2 vale anche, ma ha dichiarato con la firma della funzione ovvero "(Int => Boolean)", quindi non è di tipo Int. È una funzione e il suo valore è impostato seguendo l'espressione
{
println("val");
(x => x % 2 == 0)
}
Come per la proprietà Scala val, non è possibile assegnare un'altra funzione a even2, stessa regola di sq.
Perché non chiamare ripetutamente la funzione val di eval2 val?
Codice orig:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Sappiamo che in Scala l'ultima affermazione del precedente tipo di espressione (dentro {..}) è in realtà tornare alla sinistra. Quindi finisci per impostare even2 su "x => x% 2 == 0", che corrisponde al tipo che hai dichiarato per even2 val type ie (Int => Boolean), quindi il compilatore è contento. Ora even2 punta solo alla funzione "(x => x% 2 == 0)" (non qualsiasi altra istruzione prima di es. Println ("val") ecc. Invocare event2 con parametri diversi in realtà invocherà "(x => x% 2 == 0) "codice, poiché solo quello viene salvato con event2.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Giusto per chiarire questo, di seguito è riportata una versione diversa del codice.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Cosa accadrà ? qui vediamo "inside final fn" stampato ancora e ancora, quando chiami even2 ().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
L'esecuzione di una definizione come def x = enon valuterà l'espressione e. Invece e viene valutato ogni volta che viene invocato x.
In alternativa, Scala offre una definizione di valore
val x = e, che valuta il lato destro come parte della valutazione della definizione. Se x viene quindi utilizzato successivamente, viene immediatamente sostituito dal valore pre-calcolato di e, quindi l'espressione non deve essere valutata di nuovo.
inoltre, Val è una valutazione in base al valore. Ciò significa che l'espressione sul lato destro viene valutata durante la definizione. Dove Def è per valutazione del nome. Non valuterà fino a quando non verrà utilizzato.
Oltre alle risposte utili di cui sopra, i miei risultati sono:
def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int
def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int
def test3(): Int = 4
--test3: test3[]() => Int
Quanto sopra mostra che "def" è un metodo (con parametri argomento zero) che restituisce un'altra funzione "Int => Int" quando viene invocato.
La conversione dei metodi in funzioni è ben spiegata qui: https://tpolecat.github.io/2014/06/09/methods-functions.html
In REPL,
scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean
scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8
def significa call-by-name, valutato su richiesta
val significa call-by-value, valutato durante l'inizializzazione
Int => Booleansignifica? Penso che la sintassi di definizione siadef foo(bar: Baz): Bin = expr