Perbedaan antara tiga cara ini untuk menentukan fungsi di Scala

92

Diberikan tiga cara untuk mengekspresikan fungsi yang sama f(a) := a + 1:

val f1 = (a:Int) => a + 1
def f2 = (a:Int) => a + 1
def f3:(Int => Int) = a => a + 1

Apa perbedaan definisi ini? REPL tidak menunjukkan perbedaan yang jelas:

scala> f1
res38: (Int) => Int = <function1>
scala> f2
res39: (Int) => Int = <function1>
scala> f3
res40: (Int) => Int = <function1>
qrest
sumber
11
Anda harus mencatat bahwa di blok ke-2 di atas, mengevaluasi f1di REPL menunjukkan nilai yang terikat secara statis f1saat mengevaluasi f2dan f3menampilkan hasil pemanggilan metode tersebut. Secara khusus, Function1[Int, Int]instance baru diproduksi setiap kali salah satu f2atau f3dipanggil, sementara f1itu sama Function1[Int, Int]selamanya.
Randall Schulz
@RandallSchulz mengingat bahwa versi val tidak memerlukan contoh fungsi baru, mengapa seseorang menggunakan def dalam kasus ini?
virtualeyes
2
@virtualeyes Satu-satunya situasi yang dapat saya ingat di mana seseorang melihat def menghasilkan nilai FunctionN [...] adalah di pustaka parser kombinator. Sangat tidak umum untuk menulis metode yang menghasilkan fungsi dan secara virtual tidak akan pernah menggunakan def untuk menghasilkan banyak salinan dari fungsi yang tidak berubah secara semantik / fungsional.
Randall Schulz

Jawaban:

112

f1 adalah fungsi yang menggunakan integer dan mengembalikan integer.

f2adalah metode dengan nol arity yang mengembalikan fungsi yang mengambil integer dan mengembalikan integer. (Saat Anda mengetik f2di REPL nanti, itu menjadi panggilan ke metode f2.)

f3sama seperti f2. Anda hanya tidak menggunakan inferensi tipe di sana.

missingfaktor
sumber
6
Mengapa f1a functiondan f2apakah a method?
Freewind
17
@Freewind, fungsi adalah objek dengan metode bernama apply. Metode, yah, adalah metode.
missingfaktor
Jawaban yang luar biasa. Pertanyaan: Anda mengatakan f2 memiliki aritas nol, tetapi bukankah itu unary? en.wikipedia.org/wiki/Arity "Fungsi nullary tidak membutuhkan argumen. Fungsi unary membutuhkan satu argumen." Hanya penasaran!
Matthew Cornell
5
@MatthewCornell f2sendiri tidak menerima argumen. Objek fungsi yang dikembalikan tidak.
missingfaktor
122

Di dalam kelas, valdievaluasi pada inisialisasi sementara defdievaluasi hanya ketika, dan setiap kali , fungsi dipanggil. Pada kode di bawah ini Anda akan melihat bahwa x dievaluasi saat pertama kali objek digunakan, tetapi tidak lagi saat anggota x diakses. Sebaliknya, y tidak dievaluasi saat objek dibuat, tetapi dievaluasi setiap kali anggota diakses.

  class A(a: Int) {
    val x = { println("x is set to something"); a }
    def y = { println("y is set to something"); a }
  }

  // Prints: x is set to something
  val a = new A(1)

  // Prints: "1"
  println(a.x)

  // Prints: "1"                               
  println(a.x)

  // Prints: "y is set to something" and "1"                                  
  println(a.y)

  // Prints: "y is set to something" and "1"                                                                                   
  println(a.y)
Mendongkrak
sumber
@ JacobusR apakah ini benar hanya di dalam kelas?
Andrew Cassidy
Misalnya: scala> var b = 5 b: Int = 5 scala> val a: (Int => Int) = x => x + ba: Int => Int = <function1> scala> a (5) res48: Int = 10 scala> b = 6 b: Int = 6 scala> a (5) res49: Int = 11 Saya mengharapkan a (5) mengembalikan 10 dan nilai b telah inline
Andrew Cassidy
@AndrewCassidy fungsinya atidak dapat diubah dan dievaluasi saat inisialisasi, tetapi btetap merupakan nilai yang dapat berubah. Jadi referensi ke bdisetel selama inisialisasi, tetapi nilai yang disimpan oleh btetap bisa berubah. Untuk bersenang-senang, sekarang Anda dapat membuat file baru val b = 123. Setelah ini Anda a(5)akan selalu memberi 11, karena bsekarang adalah nilai yang sama sekali baru.
Jack
@JacobusR terima kasih ... ini masuk akal. Ini bertepatan dengan definisi "lingkup leksikal" karena fungsi a membawa referensi ke "var b" asli. Saya rasa yang membuat saya bingung adalah mengatakan: var b = 5; val c = b; b = 6; bertindak secara berbeda. Saya kira saya seharusnya tidak mengharapkan definisi fungsi yang membawa referensi ke lingkup "leksikal" asli untuk berperilaku dengan cara yang sama seperti Int.
Andrew Cassidy
3

Menjalankan definisi seperti def x = e tidak akan mengevaluasi ekspresi e . Sebaliknya e dievaluasi setiap kali x digunakan. Alternatifnya, Scala menawarkan definisi nilai val x = e , yang mengevaluasi sisi kanan e sebagai bagian dari evaluasi definisi. Jika x kemudian digunakan kemudian, itu segera diganti dengan nilai e yang telah dihitung sebelumnya , sehingga ekspresi tersebut tidak perlu dievaluasi lagi.

Scala By Example oleh Martin Odersky

Alexander
sumber