Apa perbedaan antara:
def even: Int => Boolean = _ % 2 == 0
dan
val even: Int => Boolean = _ % 2 == 0
Keduanya dapat disebut seperti bahkan(10)
.
Metode def bahkan
mengevaluasi dan menciptakan fungsi baru setiap waktu (contoh baru dari Function1
).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
Dengan def
, anda bisa mendapatkan fungsi baru pada setiap panggilan:
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
mengevaluasi bila didefinisikan, def
- ketika disebut:
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
Perhatikan bahwa ada pilihan ketiga: malas val
.
Mengevaluasi saat dipanggil pertama kali:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Tapi kembali hasil yang sama (dalam hal ini contoh sama FunctionN
) setiap kali:
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
Kinerja
val
mengevaluasi ketika didefinisikan.
def
mengevaluasi pada setiap panggilan, sehingga kinerja bisa lebih buruk dari val
untuk beberapa panggilan. Anda'll mendapatkan kinerja yang sama dengan satu panggilan. Dan dengan tidak ada panggilan anda'll mendapatkan tidak ada biaya overhead dari def
, sehingga anda dapat menentukan hal itu bahkan jika anda tidak akan menggunakannya dalam beberapa cabang.
Dengan malas val
anda'll mendapatkan malas evaluasi: anda dapat menentukan hal itu bahkan jika anda tidak akan menggunakannya dalam beberapa cabang, dan mengevaluasi sekali atau tidak pernah, tapi anda'll mendapatkan sedikit overhead dari double check penguncian pada setiap akses ke anda malas val
.
Seperti @SargeBorsch mencatat anda bisa menentukan metode, dan ini adalah pilihan tercepat:
def even(i: Int): Boolean = i % 2 == 0
Tetapi jika anda membutuhkan sebuah fungsi (metode tidak) untuk fungsi komposisi atau fungsi orde tinggi (seperti filter(bahkan)
) compiler akan menghasilkan sebuah fungsi dari metode anda setiap kali anda menggunakannya sebagai fungsi, sehingga kinerja bisa sedikit lebih buruk dibandingkan dengan val
.
Pertimbangkan ini:
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
Apakah anda melihat perbedaan? Singkatnya:
def: Untuk setiap panggilan untuk bahkan
, itu panggilan tubuh bahkan
metode lagi. Tapi dengan even2
yaitu val, fungsi ini dijalankan hanya sekali saat deklarasi (dan karena itu cetakan val
pada baris 4 dan tidak pernah lagi) dan output yang sama digunakan setiap kali diakses. Misalnya, coba lakukan ini:
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
Ketika x
diinisialisasi, nilai yang dikembalikan oleh Acak.nextInt
ditetapkan sebagai nilai akhir dari x
. Waktu berikutnya x
digunakan lagi, itu akan selalu mengembalikan nilai yang sama.
Anda dapat juga malas menginisialisasi x
. yaitu pertama waktu yang digunakan adalah diinisialisasi dan tidak saat deklarasi. Misalnya:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
Melihat ini:
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)
Anehnya, ini akan mencetak 4 dan 9! val (bahkan var) dievaluasi dan segera ditetapkan.
Sekarang perubahan val untuk def.. itu akan mencetak 9! Def adalah fungsi call.. ini akan mengevaluasi setiap kali itu disebut.
val yaitu "sq" adalah dengan Scala definisi tetap. Hal ini dievaluasi tepat pada saat deklarasi, anda dapat't perubahan nanti. Dalam contoh-contoh lain, di mana even2 juga val, tapi hal ini dinyatakan dengan fungsi khas yaitu "(Int => Boolean)", sehingga tidak tipe Int. Ini adalah fungsi dan's nilai yang diatur oleh ekspresi berikut
{
println("val");
(x => x % 2 == 0)
}
Sesuai Scala val properti, anda bisa't menetapkan fungsi lain untuk even2, aturan yang sama seperti sq.
Tentang mengapa panggilan eval2 val fungsi tidak percetakan "val" lagi dan lagi ?
Orig kode:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Kita tahu, di Scala terakhir pernyataan di atas semacam ekspresi (dalam { .. }) adalah benar-benar kembali ke sisi kiri. Jadi anda akhirnya menetapkan even2 untuk "x => x % 2 == 0" fungsi, yang sesuai dengan tipe anda menyatakan untuk even2 val jenis yaitu (Int => Boolean), jadi compiler adalah bahagia. Sekarang even2 hanya poin untuk "(x => x % 2 == 0)" fungsi (tidak lain pernyataan sebelumnya yaitu println("val") dll. Memohon event2 dengan parameter yang berbeda akan benar-benar memanggil "(x => x % 2 == 0)" kode, seperti hanya yang disimpan dengan event2.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Hanya untuk memperjelas hal ini lebih lanjut, berikut adalah versi yang berbeda dari kode.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Apa yang akan terjadi ? di sini kita melihat "di dalam akhir fn" dicetak lagi dan lagi, ketika anda menelepon even2().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Pelaksana definisi seperti def x = e
tidak akan mengevaluasi ekspresi e. Dalam - manfaat e dievaluasi setiap kali x dipanggil.
Atau, Scala menawarkan nilai definisi
val x = e
,yang tidak mengevaluasi kanan-sisi sebagai bagian dari evaluasi dari definisi.
Jika x maka digunakan kemudian, ia segera digantikan oleh pra-dihitung nilai e, sehingga ekspresi tidak perlu dievaluasi lagi.
Di 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 berarti call-by-name
, dievaluasi sesuai permintaan
val berarti call-by-value
, dievaluasi saat inisialisasi
Selain di atas bermanfaat balasan, temuan saya adalah:
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
Di atas menunjukkan bahwa "def" adalah metode (dengan nol argumen parameter) yang mengembalikan fungsi lain "Int => Int" ketika dipanggil.
Konversi metode untuk fungsi ini juga dijelaskan di sini: https://tpolecat.github.io/2014/06/09/methods-functions.html