Apa perbedaan antara "def" dan "val" untuk mendefinisikan fungsi

Apa perbedaan antara:

def even: Int => Boolean = _ % 2 == 0

dan

val even: Int => Boolean = _ % 2 == 0

Keduanya dapat disebut seperti bahkan(10).

Mengomentari pertanyaan (5)
Larutan

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 = 

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.

Komentar (10)

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 = 

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 = 

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
Komentar (4)

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.

Komentar (0)

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> 
Komentar (0)

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.

Komentar (0)

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

Komentar (1)

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

Komentar (0)

juga, Val adalah dengan nilai evaluasi. Yang berarti sisi kanan ekspresi dievaluasi pada definisi. Dimana Def dengan nama evaluasi. Itu tidak akan mengevaluasi sampai's digunakan.

Komentar (0)