437 Stimmen

Was bedeuten all die symbolischen Operatoren in Scala?

Die Scala-Syntax besteht aus einer Vielzahl von Symbolen. Da diese Arten von Namen mit Hilfe von Suchmaschinen schwer zu finden sind, wäre eine umfassende Liste hilfreich.

Was sind die Symbole in Scala, und was bewirkt jedes von ihnen?

Insbesondere würde ich gerne wissen, wie -> , ||= , ++= , <= , _._ , :: y :+= .

567voto

Daniel C. Sobral Punkte 290004

Ich unterteile die Betreiber für die Zwecke des Unterrichts in vier Kategorien :

  • Schlüsselwörter/reservierte Symbole
  • Automatisch importierte Methoden
  • Gemeinsame Methoden
  • Syntaktische Zucker/Zusammensetzung

Es ist also ein Glück, dass die meisten Kategorien in der Frage vertreten sind:

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

Die genaue Bedeutung der meisten dieser Methoden hängt von der Klasse ab, die sie definiert. Zum Beispiel, <= auf Int bedeutet "weniger als oder gleich" . Die erste, -> Ich gebe im Folgenden ein Beispiel. :: ist wahrscheinlich die Methode, die auf List (obwohl es podría das gleichnamige Objekt sein), und :+= ist wahrscheinlich die Methode, die in verschiedenen Buffer Klassen.

Schauen wir sie uns also an.

Schlüsselwörter/reservierte Symbole

Es gibt einige Symbole in Scala, die besonders sind. Zwei davon gelten als richtige Schlüsselwörter, während andere einfach "reserviert" sind. Sie sind:

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

Diese sind alle Teil der Sprache und kann als solches in jedem Text gefunden werden, der die Sprache richtig beschreibt, wie z. B. Scala-Spezifikation (PDF) selbst.

Der letzte, der Unterstrich, verdient eine besondere Beschreibung, weil er so weit verbreitet ist und so viele verschiedene Bedeutungen hat. Hier ist ein Beispiel:

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

Wahrscheinlich habe ich aber eine andere Bedeutung vergessen.

Automatisch importierte Methoden

Wenn Sie also das gesuchte Symbol in der obigen Liste nicht gefunden haben, muss es sich um eine Methode oder einen Teil einer Methode handeln. Oftmals sehen Sie jedoch ein Symbol und die Dokumentation der Klasse enthält diese Methode nicht. Wenn dies der Fall ist, handelt es sich entweder um eine Komposition einer oder mehrerer Methoden mit etwas anderem, oder die Methode wurde in den Geltungsbereich importiert oder ist durch eine importierte implizite Konvertierung verfügbar.

Diese kann noch gefunden werden auf ScalaDoc Man muss nur wissen, wo man sie suchen muss. Oder, wenn das nicht möglich ist, schauen Sie sich die Index (zur Zeit unter 2.9.1 defekt, aber unter Nightly verfügbar).

Jeder Scala-Code hat drei automatische Importe:

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

Die ersten beiden machen nur Klassen und Singleton-Objekte verfügbar. Die dritte enthält alle impliziten Konvertierungen und importierten Methoden, da Predef ist selbst ein Objekt.

Blick ins Innere Predef schnell einige Symbole zeigen:

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

Jedes andere Symbol wird über ein implizite Umrechnung . Sehen Sie sich nur die Methoden an, die mit implicit die als Parameter ein Objekt des Typs erhalten, der die Methode empfängt. Zum Beispiel:

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

In dem oben genannten Fall, -> ist definiert in der Klasse ArrowAssoc durch die Methode any2ArrowAssoc die ein Objekt des Typs A donde A ist ein unbegrenzter Typparameter für dieselbe Methode.

Gemeinsame Methoden

Viele Symbole sind also einfach Methoden einer Klasse. Zum Beispiel, wenn Sie

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

Sie finden die Methode ++ direkt in der ScalaDoc für Liste . Es gibt jedoch eine Konvention, die Sie bei der Suche nach Methoden beachten müssen. Methoden, die mit einem Doppelpunkt ( : ) binden auf der rechten Seite statt der linken. Mit anderen Worten, während der obige Methodenaufruf äquivalent zu ist:

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

Hätte ich stattdessen 1 :: List(2, 3) wäre das gleichbedeutend mit:

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

Sie müssen sich also den gefundenen Typ ansehen auf der rechten Seite bei der Suche nach Methoden, die mit Doppelpunkt enden. Nehmen wir zum Beispiel:

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

Die erste Methode ( +: ) bindet nach rechts und findet sich auf List . Die zweite Methode ( :+ ) ist nur eine normale Methode und bindet an die linke Seite - wiederum an List .

Syntaktische Zucker/Zusammensetzung

Hier also ein paar syntaktische Zucker, hinter denen sich eine Methode verbergen kann:

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

Der letzte Punkt ist interessant, denn jede symbolische Methode kann auf diese Weise zu einer zuweisungsähnlichen Methode kombiniert werden.

Und natürlich gibt es verschiedene Kombinationen, die im Code vorkommen können:

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

27voto

Pablo Fernandez Punkte 98441

Ein (guter, IMO) Unterschied zwischen Scala und anderen Sprachen ist, dass Sie Ihre Methoden mit fast jedem Zeichen benennen können.

Das, was Sie aufzählen, ist keine "Interpunktion", sondern einfache Methoden, deren Verhalten von einem Objekt zum anderen variiert (obwohl es einige Konventionen gibt).

Prüfen Sie zum Beispiel die Scaladoc-Dokumentation für Liste und Sie werden einige der von Ihnen erwähnten Methoden hier finden.

Einige Dinge sind zu beachten:

  • In den meisten Fällen ist die A operator+equal B Die Kombination ergibt sich aus A = A operator B , wie im ||= o ++= Beispiele.

  • Methoden, die auf : rechtsassoziativ sind, bedeutet dies, dass A :: B ist eigentlich B.::(A) .

Die meisten Antworten finden Sie, wenn Sie in der Scala-Dokumentation nachsehen. Ein Verweis hier würde doppelten Aufwand bedeuten und schnell ins Hintertreffen geraten :)

21voto

0__ Punkte 65479

Sie können diese zunächst nach bestimmten Kriterien gruppieren. In diesem Beitrag werde ich nur den Unterstrich und den Rechtspfeil erklären.

_._ enthält einen Punkt. Ein Punkt in Scala bedeutet immer eine Methodenaufruf . Links vom Punkt steht also der Empfänger, rechts davon die Nachricht (Methodenname). Jetzt _ es un spezielles Symbol in Scala. Es gibt mehrere Beiträge darüber, zum Beispiel dieser Blogeintrag alle Anwendungsfälle. Hier ist es ein anonyme Funktionsabkürzung das ist eine Abkürzung für eine Funktion, die ein Argument annimmt und die Methode _ darauf. Jetzt _ ist keine gültige Methode, also haben Sie mit Sicherheit _._1 oder etwas Ähnliches, d.h. der Aufruf der Methode _._1 auf das Funktionsargument. _1 a _22 sind die Methoden von Tupeln, die ein bestimmtes Element eines Tupels extrahieren. Beispiel:

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

Gehen wir nun von einem Anwendungsfall für die Funktionsanwendung shortcut aus. Gegeben eine Karte, die Ganzzahlen auf Zeichenketten abbildet:

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

Wooop, schon wieder eine seltsame Zeichensetzung. Der Bindestrich und das Größer-als-Zeichen, die wie ein Rechtspfeil ist ein Operator, der eine Tuple2 . Es gibt also auch keinen Unterschied im Ergebnis des Schreibens (1, "Eins") o 1 -> "Eins" nur, dass letzteres einfacher zu lesen ist, insbesondere in einer Liste von Tupeln wie im Beispiel der Karte. Die -> ist keine Magie, sondern, wie einige andere Operatoren, verfügbar, weil man alle implizit Konvertierungen im Objekt scala.Predef in Reichweite. Die hier stattfindende Umwandlung ist

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

ArrowAssoc hat die -> Methode, die die Tuple2 . Daher 1 -> "Eins" ist eigentlich der Aufruf Predef.any2ArrowAssoc(1).->("Eins") . Nun gut. Nun zurück zu der ursprünglichen Frage mit dem Unterstrich:

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

Der Unterstrich verkürzt hier den folgenden entsprechenden Code:

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

Beachten Sie, dass die map Methode einer Map übergibt das Tupel aus Schlüssel und Wert an das Funktionsargument. Da wir nur an den Werten (den Strings) interessiert sind, extrahieren wir sie mit der Methode _2 Methode für das Tupel.

16voto

om-nom-nom Punkte 61437

Als Ergänzung zu den brillanten Antworten von Daniel und 0__ muss ich sagen, dass Scala versteht Unicode Entsprechungen für einige der Symbole, so dass anstelle von

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

man kann schreiben

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

10voto

0__ Punkte 65479

Betreffend :: gibt es eine weitere Stackoverflow Eintrag, der sich auf die :: Fall. Kurz gesagt, es wird verwendet, um zu konstruieren Lists von ' trösten ' ein Kopfelement und eine Endliste. Es ist sowohl ein Klasse die eine konsentierte Liste darstellt und als Extraktor verwendet werden kann, aber meistens ist es ein Methode auf eine Liste. Wie Pablo Fernandez feststellt, handelt es sich, da es mit einem Doppelpunkt endet, um rechts assoziativ Das bedeutet, dass der Empfänger des Methodenaufrufs rechts und das Argument links vom Operator steht. Auf diese Weise können Sie den Konsing elegant ausdrücken als vorangestellt ein neues Kopfelement zu einer bestehenden Liste:

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

Dies ist gleichbedeutend mit

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

Die Verwendung als Extraktorobjekt ist wie folgt:

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"

Dies sieht hier wie ein Operator aus, ist aber in Wirklichkeit nur eine andere (besser lesbare) Schreibweise für

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

Mehr über Extraktoren erfahren Sie unter diese Stelle .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X