Apa perbedaan antara:
def even: Int => Boolean = _ % 2 == 0
dan
val even: Int => Boolean = _ % 2 == 0
Keduanya bisa disebut suka even(10)
.
Apa perbedaan antara:
def even: Int => Boolean = _ % 2 == 0
dan
val even: Int => Boolean = _ % 2 == 0
Keduanya bisa disebut suka even(10)
.
Jawaban:
Metode def even
mengevaluasi panggilan dan membuat fungsi baru setiap waktu (instance baru 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 di 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 ketika didefinisikan, def
- ketika dipanggil:
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: lazy val
.
Ini mengevaluasi ketika dipanggil pertama kali:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Tetapi mengembalikan hasil yang sama (dalam hal ini contoh yang 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
Performa
val
mengevaluasi ketika didefinisikan.
def
mengevaluasi setiap panggilan, sehingga kinerjanya bisa lebih buruk daripada val
untuk beberapa panggilan. Anda akan mendapatkan kinerja yang sama dengan satu panggilan. Dan tanpa panggilan, Anda tidak akan mendapatkan overhead def
, sehingga Anda dapat menentukannya meskipun Anda tidak akan menggunakannya di beberapa cabang.
Dengan lazy val
Anda akan mendapatkan evaluasi malas: Anda dapat mendefinisikannya bahkan jika Anda tidak akan menggunakannya di beberapa cabang, dan itu mengevaluasi sekali atau tidak pernah, tetapi Anda akan mendapatkan sedikit overhead dari penguncian cek ganda pada setiap akses ke Anda lazy val
.
Seperti @SargeBorsch catat, Anda dapat mendefinisikan metode, dan ini adalah opsi tercepat:
def even(i: Int): Boolean = i % 2 == 0
Tetapi jika Anda memerlukan fungsi (bukan metode) untuk komposisi fungsi atau untuk fungsi urutan yang lebih tinggi (seperti filter(even)
) kompiler akan menghasilkan fungsi dari metode Anda setiap kali Anda menggunakannya sebagai fungsi, sehingga kinerja bisa sedikit lebih buruk daripada dengan val
.
even
dipanggil.
def
dapat digunakan untuk mendefinisikan metode, dan ini adalah opsi tercepat. @ A.Karimi
even eq even
,.
@inline
atribut untuk ini. Tapi itu tidak bisa inline fungsi karena pemanggilan fungsi adalah panggilan ke apply
metode virtual objek fungsi. JVM mungkin melakukan devirtualise dan inline panggilan semacam itu dalam beberapa situasi, tetapi tidak secara umum.
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 perbedaannya? Pendeknya:
def : Untuk setiap panggilan even
, ia memanggil tubuh even
metode lagi. Tetapi dengan valeven2
yaitu , fungsi diinisialisasi hanya sekali saat deklarasi (dan karenanya ia mencetak pada baris 4 dan tidak pernah lagi) dan output yang sama digunakan setiap kali diakses. Misalnya coba lakukan ini:val
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 Random.nextInt
ditetapkan sebagai nilai akhir dari x
. Lain kali x
digunakan lagi, itu akan selalu mengembalikan nilai yang sama.
Anda juga dapat dengan malas menginisialisasi x
. yaitu pertama kali digunakan itu diinisialisasi dan bukan saat deklarasi. Sebagai contoh:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
dua kali, sekali dengan 1
dan sekali dengan 2
. Anda akan mendapatkan jawaban berbeda di setiap panggilan. Jadi, sementara println
tidak dieksekusi di panggilan berikutnya, Anda tidak mendapatkan hasil yang sama dari panggilan yang berbeda even2
. Mengapa println
tidak dieksekusi lagi, itu pertanyaan yang berbeda.
Lihat 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 bukan 9! val (even var) dievaluasi segera dan ditugaskan.
Sekarang ganti val ke def .. ini akan mencetak 9! Def adalah panggilan fungsi .. itu akan mengevaluasi setiap kali dipanggil.
val yaitu "sq" adalah dengan definisi Scala diperbaiki. Itu dievaluasi tepat pada saat deklarasi, Anda tidak dapat mengubahnya nanti. Dalam contoh lain, di mana even2 juga val, tetapi dinyatakan dengan tanda tangan fungsi yaitu "(Int => Boolean)", jadi itu bukan tipe Int. Ini adalah fungsi dan nilainya diatur dengan mengikuti ekspresi
{
println("val");
(x => x % 2 == 0)
}
Sesuai properti Scala val, Anda tidak dapat menetapkan fungsi lain ke even2, aturan yang sama dengan sq.
Tentang mengapa memanggil fungsi val eval2 tidak mencetak "val" lagi dan lagi?
Kode asal:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Kita tahu, dalam Scala pernyataan terakhir tentang jenis ekspresi di atas (di dalam {..}) sebenarnya kembali ke sisi kiri. Jadi Anda akhirnya menetapkan even2 ke fungsi "x => x% 2 == 0", yang cocok dengan tipe yang Anda nyatakan untuk tipe val even2 yaitu (Int => Boolean), jadi kompiler senang. Sekarang even2 hanya menunjuk ke fungsi "(x => x% 2 == 0)" (bukan pernyataan lain sebelumnya yaitu println ("val") dll. Memanggil event2 dengan parameter yang berbeda akan benar-benar memanggil "(x => x% 2 == 0) "kode, karena hanya kode yang disimpan dengan event2.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Hanya untuk memperjelas ini, berikut adalah versi kode yang berbeda.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Apa yang akan terjadi ? di sini kita melihat "inside final fn" dicetak berulang kali, ketika Anda memanggil even2 ().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Mengeksekusi definisi seperti def x = e
tidak akan mengevaluasi ekspresi e. Sebagai gantinya e dievaluasi setiap kali x dipanggil.
Sebagai alternatif, Scala menawarkan definisi nilai
val x = e
, yang mengevaluasi sisi kanan sebagai bagian dari evaluasi definisi. Jika x kemudian digunakan kemudian, itu segera diganti dengan nilai pra-dihitung dari e, sehingga ekspresi tidak perlu dievaluasi lagi.
juga, Val adalah evaluasi nilai. Yang berarti ekspresi sisi kanan dievaluasi selama definisi. Di mana Def adalah dengan evaluasi nama. Itu tidak akan mengevaluasi sampai digunakan.
Selain balasan yang bermanfaat di atas, 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 parameter argumen nol) yang mengembalikan fungsi lain "Int => Int" ketika dipanggil.
Konversi metode menjadi fungsi dijelaskan dengan baik di sini: https://tpolecat.github.io/2014/06/09/methods-functions.html
Dalam 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 berdasarkan permintaan
val berarti call-by-value
, dievaluasi saat inisialisasi
Int => Boolean
artinya? Saya pikir sintaks define adalahdef foo(bar: Baz): Bin = expr