Perbedaan antara metode dan fungsi dalam Scala


254

Saya membaca Fungsi Scala (bagian dari tur Scala lainnya ). Dalam pos itu ia menyatakan:

Metode dan fungsi bukan hal yang sama

Tetapi dia tidak menjelaskan apa-apa tentang itu. Apa yang dia katakan?



3
Saya pikir Anda bisa mendapatkan sesuatu dari Apa perbedaan antara metode dan fungsi
jinglining

Sebuah pertanyaan lanjutan dengan jawaban yang baik: Fungsi vs metode di Scala
Josiah Yoder

Jawaban:


238

Jim telah membahas hal ini cukup banyak di posting blognya , tapi saya memposting briefing di sini untuk referensi.

Pertama, mari kita lihat apa yang dikatakan Spesifikasi Scala. Bab 3 (tipe) memberi tahu kita tentang Tipe Fungsi (3.2.9) dan Tipe Metode (3.3.1). Bab 4 (deklarasi dasar) berbicara tentang Deklarasi Nilai dan Definisi (4.1), Deklarasi dan Definisi Variabel (4.2) dan Deklarasi dan Definisi Fungsi (4.6). Bab 6 (ekspresi) berbicara tentang Fungsi Anonim (6.23) dan Nilai Metode (6.7). Anehnya, nilai-nilai fungsi dibicarakan satu kali pada 3.2.9, dan tidak ada tempat lain.

Sebuah Fungsi Type adalah (kira-kira) jenis bentuk (T1, ..., Tn) => U , yang merupakan singkatan untuk sifat FunctionNdi perpustakaan standar. Fungsi dan Metode Anonim Nilai memiliki tipe fungsi, dan tipe fungsi dapat digunakan sebagai bagian dari nilai, deklarasi dan definisi variabel dan fungsi. Bahkan, itu bisa menjadi bagian dari tipe metode.

Sebuah Metode Type adalah jenis non-nilai . Itu berarti tidak ada nilai - tidak ada objek, tidak ada contoh - dengan tipe metode. Seperti disebutkan di atas, Nilai Metode sebenarnya memiliki Tipe Fungsi . Tipe metode adalah defdeklarasi - segala sesuatu tentang defkecuali tubuhnya.

Nilai Deklarasi dan Definisi dan Deklarasi Variabel dan Definisi yang valdan vardeklarasi, termasuk kedua jenis dan nilai - yang dapat, masing-masing, Fungsi Jenis dan Fungsi Anonymous atau Nilai Metode . Perhatikan bahwa, pada JVM, ini (nilai metode) diimplementasikan dengan apa yang disebut Java "metode".

Sebuah Fungsi Deklarasi adalah defdeklarasi, termasuk jenis dan tubuh . Bagian tipe adalah Tipe Metode, dan tubuh adalah ekspresi atau blok . Ini juga diimplementasikan pada JVM dengan apa yang disebut Java "metode".

Akhirnya, Fungsi Anonim adalah turunan dari Tipe Fungsi (yaitu, turunan dari sifat tersebut FunctionN), dan Nilai Metode adalah hal yang sama! Perbedaannya adalah bahwa Nilai Metode dibuat dari metode, baik dengan postfixing garis bawah ( m _adalah nilai metode yang sesuai dengan "deklarasi fungsi" ( def) m), atau dengan proses yang disebut ekspansi eta , yang seperti pemeran otomatis dari metode berfungsi.

Itulah yang dikatakan oleh spesifikasi, jadi izinkan saya menjelaskannya : kita tidak menggunakan terminologi itu! Ini menyebabkan terlalu banyak kebingungan antara apa yang disebut "deklarasi fungsi" , yang merupakan bagian dari program (bab 4 - deklarasi dasar) dan "fungsi anonim" , yang merupakan ekspresi, dan "tipe fungsi" , yaitu, baik tipe - sifat.

Terminologi di bawah ini, dan digunakan oleh programmer Scala yang berpengalaman, membuat satu perubahan dari terminologi spesifikasi: alih-alih mengatakan deklarasi fungsi , kami mengatakan metode . Atau bahkan deklarasi metode. Lebih lanjut, kami mencatat bahwa deklarasi nilai dan deklarasi variabel juga merupakan metode untuk tujuan praktis.

Jadi, mengingat perubahan terminologi di atas, inilah penjelasan praktis tentang perbedaan tersebut.

Sebuah fungsi adalah obyek yang termasuk salah satu FunctionXciri-ciri, seperti Function0, Function1, Function2, dll mungkin termasuk PartialFunctionjuga, yang benar-benar meluas Function1.

Mari kita lihat jenis tanda tangan untuk salah satu ciri berikut:

trait Function2[-T1, -T2, +R] extends AnyRef

Ciri ini memiliki satu metode abstrak (ia memiliki beberapa metode konkret juga):

def apply(v1: T1, v2: T2): R

Dan itu memberi tahu kita semua yang perlu diketahui tentang hal itu. Sebuah fungsi memiliki applymetode yang menerima N parameter jenis T1 , T2 , ..., TN , dan kembali sesuatu dari jenis R. Ini bertentangan pada parameter yang diterimanya, dan ko-varian pada hasilnya.

Varians itu berarti bahwa a Function1[Seq[T], String]adalah subtipe dari Function1[List[T], AnyRef]. Menjadi subtipe berarti dapat digunakan sebagai pengganti . Orang dapat dengan mudah melihat bahwa jika saya akan menelepon f(List(1, 2, 3))dan mengharapkan AnyRefkembali, salah satu dari dua jenis di atas akan berfungsi.

Sekarang, apa kesamaan metode dan fungsi? Nah, jika ffungsi dan mmetode lokal untuk ruang lingkup, maka keduanya bisa disebut seperti ini:

val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))

Panggilan-panggilan ini sebenarnya berbeda, karena yang pertama hanyalah gula sintaksis. Scala mengembangkannya ke:

val o1 = f.apply(List(1, 2, 3))

Yang, tentu saja, adalah pemanggilan metode pada objek f. Fungsinya juga memiliki gula sintaksis lainnya untuk keuntungannya: fungsi literal (sebenarnya dua di antaranya) dan (T1, T2) => Rketik tanda tangan. Sebagai contoh:

val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
  case i: Int => "Int"
  case d: Double => "Double"
  case o => "Other"
}

Kesamaan lain antara metode dan fungsi adalah bahwa yang pertama dapat dengan mudah dikonversi menjadi yang terakhir:

val f = m _

Scala akan memperluas itu , dengan asumsi mtipe (List[Int])AnyRefmenjadi (Scala 2.7):

val f = new AnyRef with Function1[List[Int], AnyRef] {
  def apply(x$1: List[Int]) = this.m(x$1)
}

Pada Scala 2.8, ini sebenarnya menggunakan AbstractFunction1kelas untuk mengurangi ukuran kelas.

Perhatikan bahwa seseorang tidak dapat mengonversi sebaliknya - dari suatu fungsi ke metode.

Metode, bagaimanapun, memiliki satu keuntungan besar (baik, dua - mereka bisa sedikit lebih cepat): mereka dapat menerima parameter tipe . Misalnya, sementara di fatas dapat menentukan jenis Listyang diterimanya ( List[Int]dalam contoh), mdapat parameterisasi:

def m[T](l: List[T]): String = l mkString ""

Saya pikir ini cukup banyak mencakup segalanya, tetapi saya akan dengan senang hati melengkapi ini dengan jawaban atas pertanyaan yang mungkin tersisa.


26
Penjelasan ini sangat jelas. Sudah selesai dilakukan dengan baik. Sayangnya buku Odersky / Venners / Spoon dan Scala spec menggunakan kata "fungsi" dan "metode" yang agak bergantian. (Mereka mungkin mengatakan "fungsi" di mana "metode" akan lebih jelas, tetapi kadang-kadang terjadi sebaliknya, misalnya, bagian 6.7 dari spesifikasi, yang mencakup metode konversi menjadi fungsi, dinamai "Nilai Metode". Ugh .) Saya pikir bahwa penggunaan kata-kata ini secara longgar telah menyebabkan banyak kebingungan ketika orang mencoba mempelajari bahasa tersebut.
Seth Tisue

4
@Seth saya tahu, saya tahu - PinS adalah buku yang mengajari saya Scala. Saya belajar lebih baik dengan cara yang sulit, yaitu, paulp meluruskan saya.
Daniel C. Sobral

4
Penjelasan hebat! Saya punya satu hal untuk ditambahkan: Ketika Anda mengutip ekspansi val f = moleh kompiler seperti yang val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }Anda harus tunjukkan bahwa di thisdalam applymetode tidak merujuk ke AnyRefobjek, tetapi ke objek yang metodenya val f = m _dievaluasi ( luar this , jadi bisa dikatakan ), karena thisberada di antara nilai-nilai yang ditangkap oleh penutupan (seperti misalnya returnseperti yang ditunjukkan di bawah).
Holger Peine

1
@ DanielC.Sobral, apa buku PinS yang Anda sebutkan? Saya juga tertarik untuk belajar Scala, dan belum menemukan buku dengan nama itu,
tldr

5
Pemrograman @tldr di Scala , oleh Odersky et all. Ini adalah singkatan yang umum untuk itu (mereka mengatakan kepada saya bahwa mereka tidak begitu menyukai PiS karena suatu alasan! :)
Daniel C. Sobral

67

Satu perbedaan praktis besar antara metode dan fungsi adalah apa returnartinya. returnhanya pernah kembali dari suatu metode. Sebagai contoh:

scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
       val f = () => { return "test" }
                       ^

Kembali dari fungsi yang didefinisikan dalam suatu metode menghasilkan pengembalian non-lokal:

scala> def f: String = {                 
     |    val g = () => { return "test" }
     | g()                               
     | "not this"
     | }
f: String

scala> f
res4: String = test

Sedangkan kembali dari metode lokal hanya kembali dari metode itu.

scala> def f2: String = {         
     | def g(): String = { return "test" }
     | g()
     | "is this"
     | }
f2: String

scala> f2
res5: String = is this

9
Itu karena pengembalian ditangkap oleh penutupan.
Daniel C. Sobral

4
Saya tidak dapat memikirkan satu kali pun saya ingin 'kembali' dari fungsi ke lingkup nonlokal. Bahkan, saya bisa melihat itu sebagai masalah keamanan yang serius jika suatu fungsi bisa memutuskan ingin kembali ke stack. Rasanya seperti longjmp, satu-satunya cara lebih mudah untuk mendapatkan kesalahan secara tidak sengaja. Saya perhatikan scalac tidak akan membiarkan saya kembali dari fungsi. Apakah itu berarti kekejian ini telah dilepaskan dari bahasa?
root

2
@ akar - bagaimana dengan kembali dari dalam for (a <- List(1, 2, 3)) { return ... }? Itu akan ditutup untuk penutupan.
Ben Lings

Hmm ... Ya, itu kasus penggunaan yang masuk akal. Masih memiliki potensi untuk mengarah ke masalah-masalah mengerikan yang sulit untuk di-debug, tetapi hal itu menempatkannya dalam konteks yang lebih masuk akal.
root

1
Jujur saya akan menggunakan sintaks yang berbeda. telah returnmengembalikan nilai dari fungsi, dan beberapa bentuk escapeatau breakatau continueuntuk kembali dari metode.
Ryan The Leach

38

function Suatu fungsi dapat dipanggil dengan daftar argumen untuk menghasilkan suatu hasil. Fungsi memiliki daftar parameter, badan, dan tipe hasil. Fungsi yang merupakan anggota dari kelas, sifat, atau objek tunggal disebut metode . Fungsi yang didefinisikan di dalam fungsi lain disebut fungsi lokal. Fungsi dengan tipe hasil Unit disebut prosedur. Fungsi anonim dalam kode sumber disebut literal fungsi. Pada saat dijalankan, fungsi literal dipakai menjadi objek yang disebut nilai fungsi.

Pemrograman dalam Scala Edisi Kedua. Martin Odersky - Lex Spoon - Bill Venners


1
Suatu fungsi dapat menjadi milik kelas sebagai def atau sebagai val / var. Hanya kekurangannya yang merupakan metode.
Josiah Yoder

29

Katakanlah Anda memiliki Daftar

scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)

Tentukan Metode

scala> def m1(i:Int)=i+2
m1: (i: Int)Int

Tentukan Fungsi

scala> (i:Int)=>i+2
res0: Int => Int = <function1>

scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)

Metode Menerima Argumen

scala> m1(2)
res3: Int = 4

Mendefinisikan Fungsi dengan val

scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>

Argumen untuk fungsi adalah Opsional

 scala> p(2)
    res4: Int = 4

scala> p
res5: Int => Int = <function1>

Argumen untuk Metode adalah Wajib

scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function

Periksa Tutorial berikut yang menjelaskan lewat perbedaan lain dengan contoh-contoh seperti contoh lain dari diff dengan Fungsi Metode Vs, Menggunakan fungsi sebagai Variabel, membuat fungsi yang mengembalikan fungsi


13

Fungsi tidak mendukung standar parameter. Metode lakukan. Konversi dari metode ke fungsi kehilangan default parameter. (Scala 2.8.1)


5
Apakah ada alasan untuk ini?
corazza

7

Ada artikel bagus di sini dari mana sebagian besar deskripsi saya diambil. Hanya perbandingan singkat dari Fungsi dan Metode mengenai pemahaman saya. Semoga ini bisa membantu:

Fungsi : Mereka pada dasarnya adalah objek. Lebih tepatnya, fungsi adalah objek dengan metode yang berlaku; Oleh karena itu, mereka sedikit lebih lambat daripada metode karena overhead mereka. Ini mirip dengan metode statis dalam arti bahwa mereka tidak tergantung pada objek yang akan dipanggil. Contoh sederhana dari suatu fungsi adalah seperti di bawah ini:

val f1 = (x: Int) => x + x
f1(2)  // 4

Baris di atas tidak lain adalah menugaskan satu objek ke objek lain seperti object1 = object2. Sebenarnya object2 dalam contoh kita adalah fungsi anonim dan sisi kiri mendapatkan jenis objek karena itu. Karena itu, sekarang f1 adalah objek (Fungsi). Fungsi anonim sebenarnya adalah instance dari Function1 [Int, Int] yang berarti fungsi dengan 1 parameter tipe Int dan mengembalikan nilai tipe Int. Memanggil f1 tanpa argumen akan memberi kita tanda tangan dari fungsi anonim (Int => Int =)

Metode : Mereka bukan objek tetapi ditugaskan ke instance kelas, yaitu objek. Persis sama dengan metode di java atau fungsi anggota dalam c ++ (seperti yang ditunjukkan oleh Raffi Khatchadourian dalam komentar untuk pertanyaan ini ) dan lain-lain. Contoh sederhana dari sebuah metode sama seperti di bawah ini:

def m1(x: Int) = x + x
m1(2)  // 4

Baris di atas bukanlah penetapan nilai sederhana tetapi definisi metode. Ketika Anda memanggil metode ini dengan nilai 2 seperti baris kedua, x diganti dengan 2 dan hasilnya akan dihitung dan Anda mendapatkan 4 sebagai output. Di sini Anda akan mendapatkan kesalahan jika cukup menulis m1 karena ini adalah metode dan perlu nilai input. Dengan menggunakan _ Anda dapat menetapkan metode ke fungsi seperti di bawah ini:

val f2 = m1 _  // Int => Int = <function1>

Apa artinya "menetapkan metode ke fungsi"? Apakah itu hanya berarti bahwa Anda sekarang memiliki objek yang berperilaku sama seperti metode itu?
K. M

@ KM: val f2 = m1 _ setara dengan val f2 = Function1 baru [Int, Int] {def m1 (x: Int) = x + x};
sasuke

3

Ini adalah posting yang bagus dari Rob Norris yang menjelaskan perbedaannya, ini adalah TL; DR

Metode dalam Scala bukan nilai, tetapi fungsi. Anda dapat membangun fungsi yang mendelegasikan metode menggunakan η-ekspansi (dipicu oleh trailing undererscore thingy).

dengan definisi berikut:

sebuah metode adalah sesuatu yang didefinisikan dengan def dan nilai adalah sesuatu yang Anda dapat menetapkan ke val

Singkatnya ( ekstrak dari blog ):

Ketika kita mendefinisikan suatu metode, kita melihat bahwa kita tidak dapat menetapkannya ke a val.

scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int

scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
       val f = add1

Perhatikan juga jenis dari add1yang tidak terlihat normal; Anda tidak dapat mendeklarasikan variabel tipe (n: Int)Int. Metode bukanlah nilai.

Namun, dengan menambahkan operator postfix-ekspansi (η diucapkan “eta”), kita dapat mengubah metode menjadi nilai fungsi. Perhatikan jenis f.

scala> val f = add1 _
f: Int => Int = <function1>

scala> f(3)
res0: Int = 4

Efeknya _adalah untuk melakukan yang setara dari yang berikut: kami membangun sebuah Function1instance yang mendelegasikan ke metode kami.

scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>

scala> g(3)
res18: Int = 4

1

Dalam Scala 2.13, tidak seperti fungsi, metode dapat mengambil / kembali

  • parameter tipe (metode polimorfik)
  • parameter implisit
  • tipe tergantung

Namun, pembatasan ini diangkat dalam dotty (Scala 3) oleh tipe fungsi Polymorphic # 4672 , misalnya, dotty versi 0.23.0-RC1 memungkinkan sintaks berikut

Ketikkan parameter

def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))

Parameter implisit ( parameter konteks )

def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero

Jenis tergantung

class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet

Untuk contoh lebih lanjut, lihat tes / jalankan / polymorphic-functions.scala


0

Secara praktis, seorang programmer Scala hanya perlu mengetahui tiga aturan berikut untuk menggunakan fungsi dan metode dengan benar:

  • Metode yang didefinisikan oleh defdan fungsi literal yang didefinisikan oleh =>adalah fungsi. Ini didefinisikan di halaman 143, Bab 8 dalam buku Programming in Scala, edisi ke-4.
  • Nilai fungsi adalah objek yang dapat diedarkan sebagai nilai apa pun. Fungsi literal dan fungsi yang diterapkan sebagian adalah nilai fungsi.
  • Anda dapat mengabaikan garis bawah fungsi yang diterapkan sebagian jika nilai fungsi diperlukan pada suatu titik dalam kode. Sebagai contoh:someNumber.foreach(println)

Setelah empat edisi Pemrograman dalam Scala, masih menjadi masalah bagi orang untuk membedakan dua konsep penting: fungsi dan nilai fungsi karena semua edisi tidak memberikan penjelasan yang jelas. Spesifikasi bahasa terlalu rumit. Saya menemukan aturan di atas sederhana dan akurat.

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.