Scala'nın tüm sembolik operatörleri ne anlama geliyor?


402

Scala sözdiziminin birçok sembolü vardır. Bu tür isimleri arama motorlarını kullanarak bulmak zor olduğundan, bunların kapsamlı bir listesi faydalı olacaktır.

Scala'daki sembollerin tümü nedir ve her biri ne yapar?

Özellikle, bilmek istiyorum yaklaşık ->, ||=, ++=, <=, _._, ::, ve :+=.


4
ve Merdiven 1'inci baskının endeksi, >> artima.com/pins1ed/book-index.html#indexanchor
Gene T

2
İlgili: operatör karakterleri vs alfasayısal karakterler: stackoverflow.com/questions/7656937/…
Luigi Plinge

1
Ayrıca, scalex veya merdiven kitabında (örneğin "!!") bulamadığınız "operatörler" (çoğunlukla yöntemler, birkaç sınıf isminin infix kullanıldığı) varsa, olası kaynaklar akka, scalaz ve sbt
Gene T

kullanılan sınıf adı örneği infix (Almanca) >> raichoo.blogspot.com/2010/06/spass-mit-scala-infixtypen.html
Gene T

arama motorları tarafından filtreleme sorunu ile ilgili olarak, symbolhound.com da güzel bir alternatif
Patrick Refondini

Yanıtlar:


526

Operatörleri öğretim amacıyla dört kategoriye ayırıyorum :

  • Anahtar kelimeler / ayrılmış simgeler
  • Otomatik olarak içe aktarılan yöntemler
  • Ortak yöntemler
  • Sözdizimsel şekerler / kompozisyon

O halde, çoğu kategorinin soruda temsil edilmesi şanslı:

->    // 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

Bu yöntemlerin çoğunun kesin anlamı, onları tanımlayan sınıfa bağlıdır. Örneğin, <=ilgili Intvasıtasıyla "daha az ya da eşit" . Birincisi, ->aşağıda örnek olarak vereceğim. ::muhtemelen aynı yöntemde tanımlanmış yöntemdir List( aynı adın nesnesi de olabilir ) ve :+=muhtemelen çeşitli Buffersınıflarda tanımlanan yöntemdir .

Öyleyse görelim.

Anahtar kelimeler / ayrılmış simgeler

Scala'da özel olan bazı semboller var. Bunlardan ikisi uygun anahtar kelimeler olarak kabul edilirken, diğerleri yalnızca "ayrılmış" olarak kabul edilir. Onlar:

// 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

Bunların hepsi dilin bir parçasıdır ve bu nedenle dili doğru tanımlayan herhangi bir metinde bulunabilir, örneğin Scala Spesifikasyonu (PDF).

Sonuncusu, alt çizgi, özel bir açıklamayı hak ediyor, çünkü çok yaygın olarak kullanılıyor ve çok farklı anlamları var. İşte bir örnek:

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

Yine de muhtemelen başka bir anlam unuttum.

Otomatik olarak içe aktarılan yöntemler

Bu nedenle, aradığınız sembolü yukarıdaki listede bulamadıysanız, bu bir yöntem veya birinin bir parçası olmalıdır. Ancak, genellikle, bazı semboller görürsünüz ve sınıfın belgelerinde bu yöntem bulunmaz. Bu olduğunda, ya bir ya da daha fazla yöntemin başka bir şeyle bir bileşimine bakıyorsunuz ya da yöntem kapsama alınmış ya da içe aktarılan bir örtük dönüştürme yoluyla kullanılabilir.

Bunlar hala bulunabilir üzerinde ScalaDoc : sadece burada onları aramak için bilmek zorunda. Ya da başarısız olursa, dizine bakın (şu anda 2.9.1'de kırılmıştır, ancak her gece mevcuttur).

Her Scala kodunda üç otomatik ithalat vardır:

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

İlk ikisi sadece sınıfları ve singleton nesnelerini kullanılabilir kılar. Üçüncüsü Predef, bir nesnenin kendisi olduğu için tüm örtük dönüşümleri ve içe aktarılan yöntemleri içerir .

İçeriye Predefhızlıca bakmak bazı sembolleri gösterir:

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

Diğer tüm semboller, örtük bir dönüştürme yoluyla kullanılabilir hale getirilir . implicitBu alıcı ile etiketlenen metotlara , parametre olarak, metodu alan tipte bir nesneye bakmanız yeterlidir . Örneğin:

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

Yukarıdaki durumda, ->sınıfta bir nesne türünü ArrowAssocalan yöntemle tanımlanır ; buradaany2ArrowAssocAA aynı yönteme bağlı olmayan bir tür parametresidir.

Ortak yöntemler

Yani, birçok sembol bir sınıftaki yöntemlerdir. Örneğin,

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

Yöntemi ++doğrudan ScalaDoc for List'te bulabilirsiniz . Bununla birlikte, yöntemleri ararken farkında olmanız gereken bir kural vardır. İki nokta üst üste ( :) ile biten yöntemler sol yerine sağa bağlanır . Başka bir deyişle, yukarıdaki yöntem çağrısı şuna eşdeğerdir:

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

Ben bunun yerine 1 :: List(2, 3), bu eşdeğer olurdu:

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

Bu nedenle , iki nokta üst üste ile biten yöntemleri ararken sağda bulunan türe bakmanız gerekir . Örneğin şunu düşünün:

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

İlk yöntem ( +:) sağa bağlanır ve üzerinde bulunur List. İkinci yöntem ( :+) sadece normal bir yöntemdir ve sola bağlanır - tekrar, açıkList .

Sözdizimsel şekerler / kompozisyon

Yani, bir yöntemi gizleyebilecek birkaç söz dizimi şekeri:

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

Sonuncusu ilginçtir, çünkü herhangi bir sembolik yöntem bu şekilde bir atama benzeri yöntem oluşturmak için birleştirilebilir.

Ve elbette, kodda görünebilecek çeşitli kombinasyonlar var:

(_+_) // 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.

1
Şunu mu demek istediniz val c = ex(2)yerine val ex(c) = 2?
Mike Stay

3
@MikeStay Hayır, demek istedim val ex(c) = 2.
Daniel

Oh, desen eşleştirme sözdizimi kullanıyor. Teşekkürler.
Mike Stay

=> aşağıdakiler arasında kullanıldığında 'isme göre çağrı' durumunu da verir ve y: => Int 'olarak yaz
Stephen W. Wright

1
Belki de: / ve: \ gerçekten sezgisel olmayan operatörlerden bahsetmek gerekir. Yani map.foldLeft (initialVal), (initialVal: / map) ile aynıdır:: \ bunun yerine foldRight'tır.
Bay MT

24

Scala ve diğer diller arasındaki bir (iyi, IMO) fark, yöntemlerinizi neredeyse her karakterle adlandırmanıza izin vermesidir.

Numaralandırdığınız şey "noktalama işaretleri" değil, sade ve basit yöntemlerdir ve bu nedenle davranışları bir nesneden diğerine değişir (bazı kurallar olsa da).

Örneğin, Liste için Scaladoc belgelerine bakın, burada bahsettiğiniz yöntemlerden bazılarını göreceksiniz.

Akılda tutulması gereken bazı şeyler:

  • Çoğu zaman A operator+equal Bkombinasyon A = A operator B, ||=veya ++=örneklerde olduğu gibi çeviri yapar .

  • Sonuçta ortaya çıkan :yöntemler doğru ilişkiseldir, bu A :: Baslında demektir B.::(A).

Çoğu cevabı Scala belgelerine göz atarak bulacaksınız. Burada bir referans tutmak çabaları çoğaltır ve çabucak geride kalır :)


21

Bunları önce bazı kriterlere göre gruplandırabilirsiniz. Bu yazıda sadece alt çizgi karakterini ve sağ oku açıklayacağım.

_._nokta içeriyor. Scala'da bir nokta her zaman bir yöntem çağrısını gösterir . Yani dönemin solunda alıcı var ve sağında mesaj (yöntem adı) var. Şimdi _bir olan özel sembol Scala. Bu konuda birkaç gönderi var, örneğin bu blog girişi tüm kullanım örnekleri. Burada anonim bir işlev kısayolu, yani bir argüman alan ve _üzerindeki yöntemi çağıran bir fonksiyonun kısayolu . Şimdi _geçerli bir yöntem değil, bu yüzden kesinlikle görüyordunuz _._1ya da benzer bir şey, yani _._1işlev argümanında yöntemi çağırmak . _1için _22bir başlığın belirli bir öğe elde dizilerini yöntemlerdir. Misal:

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

Şimdi fonksiyon uygulaması kısayolu için bir kullanım durumu olduğunu varsayalım. Tam sayıları dizelerle eşleyen bir harita verildiğinde:

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

Wooop, garip bir noktalama işareti zaten var. Sağ oklara benzeyen kısa çizgi ve büyük karakterler, a üreten bir operatördür Tuple2. Bu yüzden ya yazmanın sonucunda ya (1, "Eins")da 1 -> "Eins"sadece ikincisinin okunması daha kolay, özellikle harita örneği gibi tuples listesinde bir fark yoktur. Bu ->sihir değildir, diğer birkaç operatör gibi, kapsamdaki nesnede tüm örtük dönüşümlere sahip olduğunuz için kullanılabilir scala.Predef. Burada gerçekleşen dönüşüm

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

Nerede ArrowAssocbulunur ->oluşturur yöntemi Tuple2. Böylece 1 -> "Eins"çağrı gerçekleşir Predef.any2ArrowAssoc(1).->("Eins"). Tamam. Şimdi alt çizgi karakteriyle orijinal soruya dönelim:

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

Buradaki alt çizgi aşağıdaki eşdeğer kodu kısaltır:

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

mapBir Harita yönteminin, anahtar ve değer demetinde işlev bağımsız değişkenine geçtiğini unutmayın . Yalnızca değerler (dizeler) ile ilgilendiğimiz için, bunları _2demet üzerindeki yöntemle çıkarırız.


+1 ->Metodu anlamaya çalışırken sorun yaşıyordum ama cümleniz "Yazmanın sonucunda hiçbir fark yok ya (1, "Eins")da 1 -> "Eins"" sözdizimini ve kullanımını anlamama yardımcı oldu.
Jesse Webb

Blog giriş bağlantınız öldü
still_learning

15

Daniel ve 0__'ın parlak cevaplarına ek olarak, Scala'nın Unicode analoglarını bazı semboller için anladığını söylemeliyim , bunun yerine

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

biri yazabilir

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

10

Durumla ilgili ::başka bir Stackoverflow girişi var ::. Kısacası, bir kafa elemanını ve bir kuyruk listesini Lists' oluşturarak ' inşa etmek için kullanılır . Her iki a, sınıf bir cons'ed listesini temsil eden ve bir özütleme olarak kullanılabilir, fakat özellikle a, ilgili bir yöntem , bir liste. Pablo Fernandez'in işaret ettiği gibi, iki nokta üst üste olduğu için sağ ilişkiseldir , yani yöntem çağrısının alıcısı sağda ve operatörün solundaki argümandır. Bu şekilde , mevcut bir listeye yeni bir başlık öğesi eklemek gibi bir önemi var.

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

Bu şuna eşdeğerdir:

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

Extractor nesnesi olarak kullanım aşağıdaki gibidir:

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"

Bu burada bir operatör gibi görünüyor, ama gerçekten sadece (daha okunabilir) bir yazma yolu

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

Çıkarıcılar hakkında daha fazla bilgiyi bu yayında bulabilirsiniz .


9

<=tıpkı "okuyacağınız" gibi: 'küçük veya eşittir'. Dolayısıyla <, >(? 'Den küçük?), ==(?' Den büyük), (eşittir?), !=(Eşit değil?), <=(Küçük veya eşit?) Listesinde bir matematik operatörüdür ve>= (büyüktür veya eşit mi?).

Bu olmamalıdır karışık ile =>bir tür olan çift sağ ok bir fonksiyonu gövdesinden bağımsız değişken listesi ayırmak için ve model eşleştirme: (a test koşulu ayırmak için kullanılır, casebir eşleşme oluştuğunda yürütülen vücuttan blok) . Önceki iki cevabımda bunun örneğini görebilirsiniz. İlk olarak, fonksiyon kullanımı:

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

bu türler belirtilmediğinden zaten kısaltılmıştır. Takip fonksiyonu olurdu

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

ve desen eşleştirme kullanımı:

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"
}

4
Bu karışıklığı önlemek için sağ çift ok (\ U21D2), tek sağ "haritalar" oku (\ U2192) ve sol tek "ok" (\ U2190) için unicode karakterlerini kullanmaya karar verdim. Scala bunu destekliyor ama bir süre denemeden biraz şüpheliydim. Bu kod noktalarını sisteminizde uygun bir tuş kombinasyonuna nasıl bağlayacağınıza bakın. OS X'te gerçekten kolaydı
Connor Doyle

5

Modern bir IDE'nin büyük scala projelerini anlamak için kritik olduğunu düşünüyorum. Bu operatörler aynı zamanda yöntem olduğu için, intellij fikrinde sadece tanımları kontrol-tıklama veya kontrol-b.

Sağa eksiler operatörüne (: :) sağ tıklayıp kontrol edebilirsiniz ve "Bu listenin başına bir öğe ekler" diyen scala javadoc'a son verebilirsiniz. Kullanıcı tanımlı işleçlerde, bulunması zor çıkarımlarda tanımlanabildikleri için bu daha da kritik hale gelir ... IDE'niz örtüklerin nerede tanımlandığını bilir.


4

Sadece diğer mükemmel cevaplara ekleyerek. Scala sık sık eleştirilen iki sembolik operatör, /:( foldLeft) ve :\( foldRight) operatörler sunar; bunlardan ilki sağ ilişkiseldir. Aşağıdaki üç ifade eşdeğerdir:

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

Bu üç gibi:

( 1 to 100 ).foldRight( 0, _+_ )
( 1 to 100 ).:\( 0 )( _+_ )
( ( 1 to 100 ) :\ 0 )( _+_ )

2

Scala, Java'nın aritmetik işleçlerinin çoğunu devralır . Bu, bitsel- veya |(tekli satır karakteri), bitsel- ve &, bitsel- özel- ^veya mantıksal (boolean) veya ||(iki satır karakteri) ve mantıksal- ve içerir &&. İlginç bir şekilde, tek karakter işleçlerini açık olarak kullanabilirsiniz boolean, böylece java'ish mantıksal işleçleri tamamen gereksizdir:

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

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

Başka bir gönderide belirtildiği gibi, eşittir işaretiyle biten çağrılar =(bu ada sahip bir yöntem yoksa!) Yeniden atanma ile çözümlenir:

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

Bu 'çift kontrol', değişmez bir koleksiyon için kolayca değiştirilebilir bir değiş tokuşun yapılmasını mümkün kılar:

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)

4
Not: Boolelarda tek ve çift karakter operatörlerinin kullanılması arasında bir fark vardır - birincisi istekli (tüm terimler değerlendirilir), sonuçta ortaya çıkan boolean biliniyorsa ikincisi erken sona erer: true | { println( "Icke" ); true }⇒ yazdırır! true || { println( "Icke" ); true }⇒ yok değil yazdırmak!
0__
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.