Apa arti semua operator simbolik Scala?

402

Sintaksis scala memiliki banyak simbol. Karena nama-nama seperti ini sulit ditemukan menggunakan mesin pencari, daftar yang lengkap akan sangat membantu.

Apa saja simbol dalam Scala, dan apa yang masing-masing lakukan?

Secara khusus, saya ingin tahu tentang ->, ||=, ++=, <=, _._, ::, dan :+=.

0__
sumber
4
dan indeks Staircase 1st edition, di >> artima.com/pins1ed/book-index.html#indexanchor
Gene T
2
Terkait: karakter operator vs karakter alfanumerik: stackoverflow.com/questions/7656937/...
Luigi Plinge
1
juga, jika ada "operator" (yang sebagian besar metode, dengan beberapa nama kelas menggunakan infix) yang tidak dapat Anda temukan di scalex atau buku tangga, misalnya "!!", kemungkinan sumber adalah skaladocs untuk akka, scalaz dan sbt
Gene T
contoh nama kelas menggunakan infix (dalam bahasa Jerman) >> raichoo.blogspot.com/2010/06/spass-mit-scala-infixtypen.html
Gene T
mengenai masalah penyaringan oleh mesin pencari, symbolhound.com juga merupakan alternatif yang bagus
Patrick Refondini

Jawaban:

526

Saya membagi operator, untuk tujuan pengajaran, menjadi empat kategori :

  • Kata kunci / simbol yang dicadangkan
  • Metode yang diimpor secara otomatis
  • Metode umum
  • Gula / komposisi sintaksis

Untungnya, sebagian besar kategori terwakili dalam pertanyaan:

->    // Automatically imported method
||=   // Syntactic sugar
++=   // Syntactic sugar/composition or common method
<=    // Common method
_._   // Typo, though it's probably based on Keyword/composition
::    // Common method
:+=   // Common method

Arti yang tepat dari sebagian besar metode ini tergantung pada kelas yang mendefinisikannya. Misalnya, <=pada Intberarti "kurang dari atau sama dengan" . Yang pertama ->,, saya akan berikan contoh di bawah ini. ::mungkin adalah metode yang didefinisikan List(meskipun itu bisa menjadi objek dengan nama yang sama), dan :+=mungkin metode yang didefinisikan pada berbagai Bufferkelas.

Jadi, mari kita lihat.

Kata kunci / simbol yang dicadangkan

Ada beberapa simbol dalam Scala yang spesial. Dua di antaranya dianggap kata kunci yang tepat, sementara yang lain hanya "dipesan". Mereka:

// Keywords
<-  // Used on for-comprehensions, to separate pattern from generator
=>  // Used for function types, function literals and import renaming

// Reserved
( )        // Delimit expressions and parameters
[ ]        // Delimit type parameters
{ }        // Delimit blocks
.          // Method call and path separator
// /* */   // Comments
#          // Used in type notations
:          // Type ascription or context bounds
<: >: <%   // Upper, lower and view bounds
<? <!      // Start token for various XML elements
" """      // Strings
'          // Indicate symbols and characters
@          // Annotations and variable binding on pattern matching
`          // Denote constant or enable arbitrary identifiers
,          // Parameter separator
;          // Statement separator
_*         // vararg expansion
_          // Many different meanings

Ini semua adalah bagian dari bahasa , dan, dengan demikian, dapat ditemukan dalam teks apa pun yang menggambarkan bahasa dengan benar, seperti Spesifikasi Scala (PDF) itu sendiri.

Yang terakhir, garis bawah, layak mendapat deskripsi khusus, karena sangat banyak digunakan, dan memiliki banyak arti berbeda. Berikut ini contohnya:

import scala._    // Wild card -- all of Scala is imported
import scala.{ Predef => _, _ } // Exception, everything except Predef
def f[M[_]]       // Higher kinded type parameter
def f(m: M[_])    // Existential type
_ + _             // Anonymous function placeholder parameter
m _               // Eta expansion of method into method value
m(_)              // Partial function application
_ => 5            // Discarded parameter
case _ =>         // Wild card pattern -- matches anything
f(xs: _*)         // Sequence xs is passed as multiple parameters to f(ys: T*)
case Seq(xs @ _*) // Identifier xs is bound to the whole matched sequence

Tapi saya mungkin lupa arti lain.

Metode yang diimpor secara otomatis

Jadi, jika Anda tidak menemukan simbol yang Anda cari dalam daftar di atas, maka itu harus berupa metode, atau bagian dari satu. Tetapi, seringkali, Anda akan melihat beberapa simbol dan dokumentasi untuk kelas tidak akan memiliki metode itu. Ketika ini terjadi, apakah Anda melihat komposisi dari satu metode atau lebih dengan sesuatu yang lain, atau metode tersebut telah diimpor ke dalam ruang lingkup, atau tersedia melalui konversi implisit yang diimpor.

Ini masih dapat ditemukan di ScalaDoc : Anda hanya harus tahu di mana mencarinya. Atau, gagal itu, lihat indeks (saat ini rusak pada 2.9.1, tetapi tersedia pada malam hari).

Setiap kode Scala memiliki tiga impor otomatis:

// Not necessarily in this order
import _root_.java.lang._      // _root_ denotes an absolute path
import _root_.scala._
import _root_.scala.Predef._

Dua yang pertama hanya menyediakan kelas dan objek tunggal. Yang ketiga berisi semua konversi implisit dan metode yang diimpor, karena Predefmerupakan objek itu sendiri.

Melihat ke dalam Predefdengan cepat menunjukkan beberapa simbol:

class <:<
class =:=
object <%<
object =:=

Simbol lain apa pun akan disediakan melalui konversi tersirat . Lihat saja metode yang ditandai dengan implicityang menerima, sebagai parameter, objek bertipe yang menerima metode. Sebagai contoh:

"a" -> 1  // Look for an implicit from String, AnyRef, Any or type parameter

Dalam kasus di atas, ->didefinisikan dalam kelas ArrowAssocmelalui metode any2ArrowAssocyang mengambil objek bertipe A, di mana Aparameter tipe tidak terikat ke metode yang sama.

Metode umum

Jadi, banyak simbol hanyalah metode di kelas. Misalnya, jika Anda melakukannya

List(1, 2) ++ List(3, 4)

Anda akan menemukan metode ini ++tepat di ScalaDoc untuk Daftar . Namun, ada satu konvensi yang harus Anda waspadai saat mencari metode. Metode yang berakhir dengan titik dua ( :) mengikat ke kanan, bukan ke kiri. Dengan kata lain, sementara pemanggilan metode di atas setara dengan:

List(1, 2).++(List(3, 4))

Sebaliknya 1 :: List(2, 3), jika saya memiliki , itu akan setara dengan:

List(2, 3).::(1)

Jadi, Anda perlu melihat jenis yang ditemukan di sebelah kanan ketika mencari metode yang berakhir dengan titik dua. Pertimbangkan, misalnya:

1 +: List(2, 3) :+ 4

Metode pertama ( +:) mengikat ke kanan, dan ditemukan pada List. Metode kedua ( :+) hanyalah metode normal, dan mengikat ke kiri - lagi, pada List.

Gula / komposisi sintaksis

Jadi, inilah beberapa gula sintaksis yang mungkin menyembunyikan metode:

class Example(arr: Array[Int] = Array.fill(5)(0)) {
  def apply(n: Int) = arr(n)
  def update(n: Int, v: Int) = arr(n) = v
  def a = arr(0); def a_=(v: Int) = arr(0) = v
  def b = arr(1); def b_=(v: Int) = arr(1) = v
  def c = arr(2); def c_=(v: Int) = arr(2) = v
  def d = arr(3); def d_=(v: Int) = arr(3) = v
  def e = arr(4); def e_=(v: Int) = arr(4) = v
  def +(v: Int) = new Example(arr map (_ + v))
  def unapply(n: Int) = if (arr.indices contains n) Some(arr(n)) else None
}

val Ex = new Example // or var for the last example
println(Ex(0))  // calls apply(0)
Ex(0) = 2       // calls update(0, 2)
Ex.b = 3        // calls b_=(3)
// This requires Ex to be a "val"
val Ex(c) = 2   // calls unapply(2) and assigns result to c
// This requires Ex to be a "var"
Ex += 1         // substituted for Ex = Ex + 1

Yang terakhir menarik, karena metode simbolis apa pun dapat digabungkan untuk membentuk metode penugasan seperti itu.

Dan, tentu saja, ada berbagai kombinasi yang dapat muncul dalam kode:

(_+_) // An expression, or parameter, that is an anonymous function with
      // two parameters, used exactly where the underscores appear, and
      // which calls the "+" method on the first parameter passing the
      // second parameter as argument.
Daniel C. Sobral
sumber
1
Apakah maksud Anda val c = ex(2)alih-alih val ex(c) = 2?
Mike Stay
3
@ MikeStay Tidak, maksud saya val ex(c) = 2.
Daniel C. Sobral
Oh, itu menggunakan sintaks pencocokan pola. Terima kasih.
Mike Stay
=> juga menganugerahkan status 'panggil nama' ketika digunakan antara: dan ketik seperti dalam y: => Int '
Stephen W. Wright
1
Mungkin kita juga harus menyebutkan operator: / dan: \ benar-benar tidak intuitif. Jadi map.foldLeft (initialVal) sama dengan (initialVal: / map) -: \ adalah foldRight sebagai gantinya.
Tn. MT
24

Satu perbedaan (baik, IMO) antara Scala dan bahasa lain adalah ia memungkinkan Anda memberi nama metode Anda dengan hampir semua karakter.

Apa yang Anda sebutkan bukanlah "tanda baca" tetapi metode sederhana dan sederhana, dan dengan demikian perilakunya bervariasi dari satu objek ke objek lainnya (meskipun ada beberapa konvensi).

Misalnya, periksa dokumentasi Scaladoc untuk Daftar , dan Anda akan melihat beberapa metode yang Anda sebutkan di sini.

Beberapa hal yang perlu diingat:

  • Sebagian besar A operator+equal Bkombinasi waktu diterjemahkan menjadi A = A operator B, seperti dalam ||=atau ++=contoh.

  • Metode yang berakhir dengan :asosiatif benar, ini berarti itu A :: Bsebenarnya B.::(A).

Anda akan menemukan sebagian besar jawaban dengan menjelajahi dokumentasi Scala. Menyimpan referensi di sini akan menggandakan upaya, dan itu akan jatuh dengan cepat :)

Pablo Fernandez
sumber
21

Anda dapat mengelompokkannya terlebih dahulu sesuai dengan beberapa kriteria. Dalam posting ini saya hanya akan menjelaskan karakter garis bawah dan panah kanan.

_._berisi titik. Periode di Scala selalu menunjukkan pemanggilan metode . Jadi kiri periode Anda memiliki penerima, dan kanannya pesan (nama metode). Sekarang _adalah simbol khusus di Scala. Ada beberapa posting tentang itu, misalnya entri blog ini semua menggunakan kasus. Ini adalah jalan pintas fungsi anonim , yang merupakan jalan pintas untuk fungsi yang mengambil satu argumen dan memanggil metode _di atasnya. Sekarang _bukan metode yang valid, jadi pasti Anda melihat _._1atau sesuatu yang serupa, yaitu, memanggil metode _._1pada argumen fungsi. _1to _22adalah metode tuple yang mengekstraksi elemen tertentu dari tuple. Contoh:

val tup = ("Hallo", 33)
tup._1 // extracts "Hallo"
tup._2 // extracts 33

Sekarang mari kita asumsikan sebuah use case untuk shortcut aplikasi fungsi. Diberi peta yang memetakan bilangan bulat ke string:

val coll = Map(1 -> "Eins", 2 -> "Zwei", 3 -> "Drei")

Wooop, sudah ada tanda baca aneh lainnya. Karakter tanda hubung dan lebih besar dari, yang menyerupai panah kanan , adalah operator yang menghasilkan a Tuple2. Jadi tidak ada perbedaan dalam hasil penulisan baik (1, "Eins")atau 1 -> "Eins", hanya bahwa yang terakhir lebih mudah dibaca, terutama dalam daftar tupel seperti contoh peta. Ini ->bukan sihir, itu, seperti beberapa operator lain, tersedia karena Anda memiliki semua konversi implisit dalam objek scala.Predefdalam ruang lingkup. Konversi yang terjadi di sini adalah

implicit def any2ArrowAssoc [A] (x: A): ArrowAssoc[A] 

Dimana ArrowAssocmemiliki ->metode yang menciptakan Tuple2. Dengan demikian 1 -> "Eins"panggilan yang sebenarnya Predef.any2ArrowAssoc(1).->("Eins"). Baik. Sekarang kembali ke pertanyaan awal dengan karakter garis bawah:

// lets create a sequence from the map by returning the
// values in reverse.
coll.map(_._2.reverse) // yields List(sniE, iewZ, ierD)

Garis bawah di sini mempersingkat kode setara berikut:

coll.map(tup => tup._2.reverse)

Perhatikan bahwa mapmetode Peta lewat dalam tupel kunci dan nilai ke argumen fungsi. Karena kami hanya tertarik pada nilai-nilai (string), kami mengekstraknya dengan _2metode pada tuple.

0__
sumber
+1 Saya mengalami kesulitan mencoba memahami ->metode ini tetapi kalimat Anda "Jadi tidak ada perbedaan dalam hasil penulisan baik (1, "Eins")atau 1 -> "Eins"" membantu saya memahami sintaks dan penggunaannya.
Jesse Webb
fyi tautan entri blog Anda mati
still_learning
15

Sebagai tambahan untuk jawaban brilian Daniel dan 0__, saya harus mengatakan bahwa Scala memahami analog Unicode untuk beberapa simbol, jadi alih-alih

for (n <- 1 to 10) n % 2 match {
  case 0 => println("even")
  case 1 => println("odd")
}

orang dapat menulis

for (n ← 1 to 10) n % 2 match {
  case 0 ⇒ println("even")
  case 1 ⇒ println("odd")
}
om-nom-nom
sumber
10

Mengenai ::ada entri Stackoverflow lain yang mencakup ::kasus ini. Singkatnya, ini digunakan untuk membangun Listsdengan ' menyetujui ' elemen kepala dan daftar ekor. Ini adalah kelas yang mewakili daftar yang diikuti dan yang dapat digunakan sebagai ekstraktor, tetapi paling umum itu adalah metode pada daftar. Seperti yang Pablo Fernandez tunjukkan, karena berakhir di titik dua, itu adalah asosiatif yang benar , artinya penerima panggilan metode ada di kanan, dan argumen di sebelah kiri operator. Dengan begitu Anda dapat mengekspresikan konsing secara elegan sebagai menambahkan elemen kepala baru ke daftar yang ada:

val x = 2 :: 3 :: Nil  // same result as List(2, 3)
val y = 1 :: x         // yields List(1, 2, 3)

Ini setara dengan

val x = Nil.::(3).::(2) // successively prepend 3 and 2 to an empty list
val y = x.::(1)         // then prepend 1

Penggunaan sebagai objek extractor adalah sebagai berikut:

def extract(l: List[Int]) = l match {
   case Nil          => "empty"
   case head :: Nil  => "exactly one element (" + head + ")"
   case head :: tail => "more than one element"
}

extract(Nil)          // yields "empty"
extract(List(1))      // yields "exactly one element (1)"
extract(List(2, 3))   // yields "more than one element"

Ini terlihat seperti operator di sini, tetapi ini sebenarnya hanyalah cara penulisan (yang lebih mudah dibaca)

def extract2(l: List[Int]) = l match {
   case Nil            => "empty"
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   case ::(head, tail) => "more than one element"
}

Anda dapat membaca lebih lanjut tentang ekstraktor di pos ini .

0__
sumber
9

<=sama seperti Anda akan "membacanya": 'kurang dari atau sama dengan'. Jadi operator matematika, dalam daftar <(kurang dari?), >(Lebih besar dari?), ==( !=Sama dengan?), (Tidak sama?), <=(Kurang dari atau sama?), Dan >=(lebih besar dari atau sama?).

Ini tidak harus bingung dengan =>yang merupakan jenis panah kanan ganda , yang digunakan untuk memisahkan daftar argumen dari tubuh fungsi dan untuk memisahkan kondisi pengujian dalam pencocokan pola ( caseblok) dari tubuh yang dieksekusi ketika pertandingan terjadi . Anda dapat melihat contoh ini di dua jawaban saya sebelumnya. Pertama, fungsinya menggunakan:

coll.map(tup => tup._2.reverse)

yang sudah disingkat jenis dihilangkan. Fungsi follow adalah

// function arguments         function body
(tup: Tuple2[Int, String]) => tup._2.reverse

dan penggunaan pencocokan pola:

def extract2(l: List[Int]) = l match {
   // if l matches Nil    return "empty"
   case Nil            => "empty"
   // etc.
   case ::(head, Nil)  => "exactly one element (" + head + ")"
   // etc.
   case ::(head, tail) => "more than one element"
}
0__
sumber
4
Menghindari kebingungan ini adalah mengapa saya memutuskan untuk mulai menggunakan karakter unicode untuk panah ganda kanan (\ U21D2), panah "peta" tunggal kanan (\ U2192), dan panah kiri tunggal "dalam" (\ U2190). Scala mendukung ini tetapi saya agak skeptis sampai saya mencobanya sebentar. Lihat saja bagaimana cara mengikat poin kode ini ke kombinasi tombol yang mudah digunakan pada sistem Anda. Itu sangat mudah pada OS X.
Connor Doyle
5

Saya menganggap IDE modern sangat penting untuk memahami proyek scala besar. Karena operator ini juga metode, dalam ide intellij saya hanya mengontrol klik atau kontrol-b ke dalam definisi.

Anda dapat mengontrol klik kanan ke operator kontra (: :) dan berakhir di scala javadoc mengatakan "Menambahkan elemen di awal daftar ini." Dalam operator yang ditentukan pengguna, ini menjadi lebih kritis, karena mereka dapat didefinisikan dalam implisit yang sulit ditemukan ... IDE Anda tahu di mana implisit didefinisikan.

nairbv
sumber
4

Hanya menambah jawaban bagus lainnya. Scala menawarkan dua operator simbolik yang sering dikritik, /:( foldLeft) dan :\( foldRight) operator, yang pertama adalah asosiatif-kanan. Jadi tiga pernyataan berikut ini setara:

( 1 to 100 ).foldLeft( 0, _+_ )
( 1 to 100 )./:( 0 )( _+_ )
( 0 /: ( 1 to 100 ) )( _+_ )

Seperti tiga ini:

( 1 to 100 ).foldRight( 0, _+_ )
( 1 to 100 ).:\( 0 )( _+_ )
( ( 1 to 100 ) :\ 0 )( _+_ )
Tuan MT
sumber
2

Scala mewarisi sebagian besar operator aritmatika Jawa . Ini termasuk bitwise-atau |(karakter pipa tunggal), bitwise-dan &, bitwise-eksklusif-atau ^, serta logis (boolean) atau ||(dua karakter pipa) dan logis-dan &&. Menariknya, Anda dapat menggunakan operator karakter tunggal boolean, sehingga operator logis java'r benar-benar berlebihan:

true && true   // valid
true & true    // valid as well

3 & 4          // bitwise-and (011 & 100 yields 000)
3 && 4         // not valid

Seperti yang ditunjukkan di pos lain, panggilan yang berakhir dengan tanda sama dengan =, diselesaikan (jika metode dengan nama itu tidak ada!) Dengan penugasan kembali:

var x = 3
x += 1         // `+=` is not a method in `int`, Scala makes it `x = x + 1`

'Pemeriksaan ulang' ini memungkinkan, untuk dengan mudah menukar yang dapat diubah untuk koleksi yang tidak berubah:

val m = collection.mutable.Set("Hallo")   // `m` a val, but holds mutable coll
var i = collection.immutable.Set("Hallo") // `i` is a var, but holds immutable coll

m += "Welt" // destructive call m.+=("Welt")
i += "Welt" // re-assignment i = i + "Welt" (creates a new immutable Set)
0__
sumber
4
PS Ada perbedaan antara menggunakan operator karakter tunggal vs ganda pada boolean — yang pertama bersemangat (semua persyaratan dievaluasi), yang terakhir berakhir lebih awal jika boolean yang dihasilkan diketahui: true | { println( "Icke" ); true }⇒ dicetak! true || { println( "Icke" ); true }tidak mencetak!
0__