Apa fungsi yang diterapkan di Scala?


311

Saya tidak pernah memahaminya dari contoh-contoh kata benda yang dibuat dan dibuat-buat (sebuah AddTwokelas memiliki applyyang menambah dua!) Contoh.

Saya mengerti bahwa itu sintaksis gula, jadi (saya menyimpulkan dari konteks) itu pasti dirancang untuk membuat beberapa kode lebih intuitif.

Apa arti yang diberikan oleh kelas dengan suatu applyfungsi? Apa yang digunakan untuk itu, dan untuk apa tujuannya membuat kode menjadi lebih baik (unmarshalling, verbing nouns, dll)?

bagaimana itu membantu ketika digunakan dalam objek pengiring?


4
Bahkan googling cepat membawa banyak artikel bagus. Ini satu: jackcoughonsoftware.blogspot.com/2009/01/…
om-nom-nom


6
sebenarnya itu sama dengan konstruktor di Java / C ++ tetapi dapat mengembalikan nilai dan memiliki nama 'apply'
ses

Ini metode, bukan fungsi. Perbedaan antara metode dan fungsi sangat penting dalam Scala.
rightfold

1
Untuk menguraikan Zoidberg, "Metode hanyalah fungsi yang dapat mengakses keadaan kelas" twitter.github.io/scala_school/basics.html Secara umum ini berlaku untuk lebih dari Scala stackoverflow.com/questions/155609/…
DharmaTurtle

Jawaban:


641

Matematikawan memiliki cara kecil mereka sendiri yang lucu, jadi alih-alih mengatakan "maka kita memanggil fungsi yang fmelewatinya xsebagai parameter" seperti yang akan dikatakan oleh programmer, mereka berbicara tentang "menerapkan fungsi fke argumennya x".

Dalam matematika dan ilmu komputer, Terapkan adalah fungsi yang menerapkan fungsi pada argumen.
Wikipedia

applymelayani tujuan menutup celah antara paradigma Berorientasi Objek dan Fungsional di Scala. Setiap fungsi dalam Scala dapat direpresentasikan sebagai objek. Setiap fungsi juga memiliki tipe OO: misalnya, sebuah fungsi yang mengambil Intparameter dan mengembalikan sebuah Intakan memiliki tipe OO Function1[Int,Int].

 // define a function in scala
 (x:Int) => x + 1

 // assign an object representing the function to a variable
 val f = (x:Int) => x + 1

Karena semuanya adalah objek di Scala fsekarang dapat diperlakukan sebagai referensi ke Function1[Int,Int]objek. Sebagai contoh, kita dapat memanggil toStringmetode yang diwarisi Any, yang tidak mungkin untuk fungsi murni, karena fungsi tidak memiliki metode:

  f.toString

Atau kita bisa mendefinisikan Function1[Int,Int]objek lain dengan memanggil composemetode fdan merantai dua fungsi yang berbeda secara bersamaan:

 val f2 = f.compose((x:Int) => x - 1)

Sekarang jika kita ingin benar-benar menjalankan fungsi, atau sebagai ahli matematika mengatakan "terapkan fungsi ke argumennya" kita akan memanggil applymetode pada Function1[Int,Int]objek:

 f2.apply(2)

Menulis f.apply(args)setiap kali Anda ingin menjalankan fungsi yang diwakili sebagai objek adalah cara yang berorientasi objek, tetapi akan menambahkan banyak kekacauan pada kode tanpa menambahkan banyak informasi tambahan dan alangkah baiknya untuk dapat menggunakan lebih banyak notasi standar, seperti sebagai f(args). Di situlah kompilator Scala melangkah masuk dan kapan pun kami memiliki referensi fke objek fungsi dan menulis f (args)untuk menerapkan argumen pada fungsi yang diwakilkan, kompiler diam-diam mengembang f (args)ke pemanggilan metode objek f.apply (args).

Setiap fungsi dalam Scala dapat diperlakukan sebagai objek dan berfungsi dengan cara lain juga - setiap objek dapat diperlakukan sebagai fungsi, asalkan memiliki applymetode. Objek tersebut dapat digunakan dalam notasi fungsi:

// we will be able to use this object as a function, as well as an object
object Foo {
  var y = 5
  def apply (x: Int) = x + y
}


Foo (1) // using Foo object in function notation 

Ada banyak kasus penggunaan ketika kita ingin memperlakukan objek sebagai fungsi. Skenario yang paling umum adalah pola pabrik . Alih-alih menambahkan kekacauan pada kode menggunakan metode pabrik, kita dapat applymenolak serangkaian argumen untuk membuat instance baru dari kelas terkait:

List(1,2,3) // same as List.apply(1,2,3) but less clutter, functional notation

// the way the factory method invocation would have looked
// in other languages with OO notation - needless clutter
List.instanceOf(1,2,3) 

Jadi applymetode hanyalah cara praktis untuk menutup celah antara fungsi dan objek di Scala.


1
bagaimana Anda menambahkan tipe variabel khusus ke objek. AFAIK itu tidak mungkin. Berikut ini sebuah contoh: Anda memiliki kelas ini class Average[YType](yZeroValue:YType). Bagaimana Anda melewati YType dari objek itu karena objek tidak dapat mengambil params jenis?
Adrian

Jenis-jenis dalam Scala biasanya dapat disimpulkan berdasarkan argumen, tetapi jika tidak, Anda dapat menyediakannya melalui tanda kurung siku. jadi ketika membuat instance objek Average Anda bisa mengatakan "val avg = new Average [Int] (0)"
Angelo Genovese

4
Kelas kasus layak disebutkan di sini. Kelas kasus dengan mudah, otomatis, dan tidak terlihat menambah applydan unapplymetode ke objek pendamping kelas (antara lain). Jadi mendefinisikan kelas dengan hanya satu baris seperti ini case class Car(make:String, model: String, year: Short, color: String)memungkinkan untuk menghasilkan objek baru dari kelas Mobil dengan hanya: val myCar = Car("VW","Golf",1999,"Blue"). Ini adalah singkatan untukCar.apply(...)
Kjetil S.

1
every object can be treated as a function, provided it has the apply method- Sekarang sangat masuk akal.
Arj


51

Itu berasal dari gagasan bahwa Anda sering ingin menerapkan sesuatu pada suatu objek. Contoh yang lebih akurat adalah pabrik. Saat Anda memiliki pabrik, Anda ingin menerapkan parameter padanya untuk membuat objek.

Orang-orang Scala berpikir bahwa, seperti yang terjadi dalam banyak situasi, mungkin lebih baik memiliki jalan pintas untuk menelepon apply. Jadi, ketika Anda memberikan parameter langsung ke objek, itu seolah-olah Anda melewati parameter ini ke fungsi yang berlaku dari objek itu:

class MyAdder(x: Int) {
  def apply(y: Int) = x + y
}

val adder = new MyAdder(2)
val result = adder(4) // equivalent to x.apply(4)

Ini sering digunakan dalam objek pendamping, untuk memberikan metode pabrik yang bagus untuk kelas atau sifat, berikut adalah contohnya:

trait A {
  val x: Int
  def myComplexStrategy: Int
}

object A {
  def apply(x: Int): A = new MyA(x)

  private class MyA(val x: Int) extends A {
    val myComplexStrategy = 42
  }
}

Dari pustaka standar scala, Anda mungkin melihat bagaimana scala.collection.Seqdiimplementasikan: Seqadalah suatu sifat, sehingga new Seq(1, 2)tidak akan mengkompilasi tetapi berkat objek pendamping dan berlaku, Anda dapat memanggil Seq(1, 2)dan implementasinya dipilih oleh objek pendamping.


Bisakah ekuivalen dari penerapan dicapai menggunakan Implicits juga?
jayunit100

11

Ini adalah contoh kecil bagi mereka yang ingin membaca dengan cepat

 object ApplyExample01 extends App {


  class Greeter1(var message: String) {
    println("A greeter-1 is being instantiated with message " + message)


  }

  class Greeter2 {


    def apply(message: String) = {
      println("A greeter-2 is being instantiated with message " + message)
    }
  }

  val g1: Greeter1 = new Greeter1("hello")
  val g2: Greeter2 = new Greeter2()

  g2("world")


} 

keluaran

Penyambut-1 sedang dipakai dengan pesan halo

Penyambut-2 sedang dipakai dengan dunia pesan


2
Apakah pesan untuk g2 benar? Apakah itu benar-benar terjadi pada instantiasi, atau itu sebenarnya setelah instantiasi (bahkan jika itu instantiated dengan malas)?
Tim Barrass
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.