Care este diferența între:
def even: Int => Boolean = _ % 2 == 0
și
val even: Int => Boolean = _ % 2 == 0
Ambele pot fi numite ca chiar(10)
.
Metoda def chiar evaluează pe apel și creează o nouă funcție de fiecare data (nou exemplu de
Function1`).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Cu def
puteți obține noua funcție pe fiecare apel:
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
"val" evaluează, atunci când sunt definite, def
- atunci când este solicitat:
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
Rețineți că există o a treia opțiune: leneș val
.
Acesta evaluează, atunci când a sunat prima dată:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Dar returnează același rezultat (în acest caz, aceeași instanță a FunctionN
), de fiecare dată:
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
Performanță
"val" evaluează, atunci când sunt definite.
def
evaluează la fiecare apel, astfel de performanță ar putea fi mai rău decât " val " pentru mai multe apeluri. Te'll obține aceeași performanță cu un singur apel. Și nu te cheamă'll nr aeriene din def
, astfel încât să puteți defini chiar dacă nu-l va folosi în unele ramuri.
Cu un leneș de valte'll obține un leneș de evaluare: puteți defini chiar dacă nu-l va folosi în unele ramuri, și se evaluează o dată sau nu, dar'll a obține un pic de regie de verificare dublu de blocare pe fiecare acces la
leneș val`.
Ca @SargeBorsch remarcat-ai putea defini metoda, și aceasta este cea mai rapidă opțiune:
def even(i: Int): Boolean = i % 2 == 0
Dar dacă ai nevoie de o funcție (nu metoda) pentru funcția de compoziție sau pentru funcții de ordin superior (cum ar fi filtru(chiar)
) compilatorul va genera o funcție de metoda ta de fiecare dată când îl utilizați-l ca funcție, astfel de performanță ar putea fi ușor mai rău decât cu "val".
Luați în considerare acest lucru:
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
Vezi diferența? Pe scurt:
def: Pentru fiecare apel de a "chiar", se solicită corpul "chiar" metoda din nou. Dar cu even2
adică val, funcția este inițializat doar o singură dată în timp ce declarația (și, prin urmare, se imprimă " val " de la linia 4 și niciodată din nou) și aceeași ieșire este utilizată de fiecare dată când este accesat. De exemplu, incearca sa faci acest lucru:
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
Atunci când " x " este inițializat, valoarea returnată de Aleatorie.nextInt` este setată ca valoare finală de "x". Data viitoare când " x " este folosit din nou, se va întoarce întotdeauna aceeași valoare.
Puteți, de asemenea, alene inițializa "x". adică prima dată când este utilizat, este inițializat și nu în timp ce declarația. De exemplu:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
Vezi asta:
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)
În mod surprinzător, acest lucru se va imprima 4 și nu 9! val (chiar var) este evaluat imediat și alocate.
Schimba acum pe val la def.. se va imprima 9! Def este un apel de funcție.. se va evalua de fiecare dată când este chemat.
val de exemplu "mp" este de Scala definiție este fix. Este evaluată corect la momentul declarației, puteți't schimba mai târziu. În alte exemple, în cazul în care even2, de asemenea, val, dar a declarat cu funcția semnătura și anume "(Int => Boolean)", așa că nu este de tip Int. Este o funcție și l's valoare este stabilită de următoarea expresie
{
println("val");
(x => x % 2 == 0)
}
Ca pe Scala val de proprietate, puteți't atribui o altă funcție a even2, aceeași regulă ca sq.
Despre ce asteptare eval2 val funcția de imprimare nu "val" din nou și din nou ?
Orig cod:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Știm, în Scala ultima declarație de mai sus un fel de exprimare (în interiorul { .. }) este, de fapt, a reveni la partea stângă. Deci ajungi stabilirea even2 la "x => x % 2 == 0" funcție, care se potrivește cu tipul declarat pentru even2 val de tip anume (Int => Boolean), deci compiler este fericit. Acum even2 doar puncte de la "(x => x % 2 == 0)" funcție (nu orice altă declarație înainte și anume println("val"), etc. Invocarea event2 cu parametri diferiți de fapt va invoca "(x => x % 2 == 0)" codul, ca doar asta este salvat cu event2.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Doar pentru a clarifica acest lucru mai mult, în urma este o altă versiune a codului.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Ce se va întâmpla ? aici vom vedea "în interiorul final fn" tipărită din nou și din nou, atunci când apelați even2().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Executare o definiție cum ar fi def x = e
nu va evalua expresia e. În locul e este evaluată ori de câte ori x este invocată.
Alternativ, Scala oferă o valoare definiție
val x = e
,care face evaluarea dreapta-side, ca parte a evaluării de definiție.
Dacă x este apoi utilizat ulterior, este imediat înlocuit de pre-calculat valoarea lui e, astfel că expresia nu trebuie să fie evaluate din nou.
În REPL,
scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean
scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/[email protected]
def înseamnă apel-cu-numele, evaluate la cerere
val înseamnă call-by-value
, evaluate în timp ce inițializarea
În plus față de cele de mai sus răspunsuri utile, concluziile mele sunt:
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
De mai sus arată că "def" este o metodă (cu zero argument parametrii) care returnează o altă funcție "Int => Int" atunci când este invocată.
Conversia metodelor de funcții este bine explicat aici: https://tpolecat.github.io/2014/06/09/methods-functions.html