Que signifient tous les opérateurs symboliques de Scala?

la syntaxe Scala a beaucoup de symboles. Comme il est difficile de trouver ce genre de noms à l'aide des moteurs de recherche, il serait utile d'en dresser une liste exhaustive.

Quels sont tous les symboles de Scala, et que font chacun d'eux?

en particulier, je voudrais savoir à propos de -> , ||= , ++= , <= , _._ , :: , et :+= .

363
demandé sur Chris Martin 2011-10-25 16:00:11

9 réponses

je divise les opérateurs, à des fins d'enseignement, en quatre catégories :

  • mots clés /symboles réservés
  • méthodes importées automatiquement
  • méthodes communes
  • sucres syntactiques / composition

il est donc heureux que la plupart des catégories soient représentées dans la question:

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

le sens exact de la plupart de ces méthodes dépend de la classe qui les définit. Par exemple, <= sur Int signifie "inférieur ou égal à" . Le premier, -> , je vais donner comme exemple ci-dessous. :: est probablement la méthode définie sur List (bien que pourrait être l'objet du même nom), et :+= est probablement la méthode définie sur diverses classes Buffer .

alors, voyons-les.

mots clés /symboles réservés

il y a quelques symboles dans Scala qui sont spéciaux. Deux d'entre eux sont considérés comme des mots clés appropriés, tandis que d'autres sont "réservés". Ce sont:

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

ce sont tous partie de la langue , et, en tant que tel, peut être trouvé dans tout texte qui décrivent correctement la langue, comme Spécification Scala (PDF) lui-même.

le dernier, le underscore, mérite une description spéciale, parce qu'il est si largement utilisé, et a tant de significations différentes. Voici un échantillon:

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

j'ai probablement oublié un autre sens.

méthodes importées automatiquement

ainsi, si vous n'avez pas trouvé le symbole que vous recherchez dans la liste ci-dessus, alors il doit être une méthode, ou une partie d'un. Mais, souvent, vous verrez un symbole et la documentation de la classe, n'auront pas cette méthode. Lorsque cela se produit, soit vous regardez une composition d'une ou plusieurs méthodes avec quelque chose d'autre, ou la méthode a été importée dans scope, ou est disponible par une conversion implicite importée.

Ces peut encore être trouvé sur ScalaDoc : vous avez juste à savoir où chercher pour eux. Ou, à défaut, regardez le index (actuellement cassé le 2.9.1, mais disponible le soir).

chaque code Scala a trois importations automatiques:

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

les deux premiers ne rendent disponibles que les classes et les objets singleton. La troisième contient toutes les conversions implicites et les méthodes importées, puisque Predef est un objet lui-même.

Regarder à l'intérieur Predef rapidement montrer quelques symboles:

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

tout autre symbole sera disponible au moyen d'une conversion implicite . Il suffit de regarder les méthodes étiquetées avec implicit qui reçoivent, comme paramètre, un objet de type qui reçoit la méthode. Par exemple:

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

dans le cas ci-dessus, -> est défini dans la classe ArrowAssoc par la méthode any2ArrowAssoc qui prend un objet de tapez A , où A est un paramètre de type unbounded à la même méthode.

méthodes communes

ainsi, beaucoup de symboles sont simplement des méthodes sur une classe. Par exemple, si vous faites

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

vous trouverez la méthode ++ directement sur le ScalaDoc pour liste . Cependant, il y a une convention que vous devez être conscient lors de la recherche de méthodes. Méthodes se terminant par deux points ( : ) liez à la droite au lieu de la gauche. En d'autres termes, alors que l'appel de méthode ci-dessus est équivalent à:

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

si j'avais, au lieu de 1 :: List(2, 3) , ce serait l'équivalent de:

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

vous devez donc regarder le type trouvé sur la droite lorsque vous cherchez des méthodes se terminant par deux points. Considérons, par exemple:

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

le premier la méthode ( +: ) se lie à droite, et se trouve sur List . La deuxième méthode ( :+ ) est une méthode normale, et se lie à la gauche, encore une fois, sur List .

sucres syntactiques / composition

donc, voici quelques sucres syntaxiques qui peuvent cacher une méthode:

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

la dernière est intéressante, parce que n'importe quelle méthode symbolique peut être combiné pour former un l'affectation comme méthode de cette façon.

et, bien sûr, il y a différentes combinaisons qui peuvent apparaître dans le code:

(_+_) // 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.
491
répondu Daniel C. Sobral 2016-07-22 18:21:27

une (bonne, IMO) différence entre Scala et d'autres langues est qu'il vous permet de nommer vos méthodes avec presque n'importe quel caractère.

ce que vous énumérez n'est pas une" ponctuation " mais des méthodes simples et simples, et en tant que telles leur comportement varie d'un objet à l'Autre (bien qu'il y ait quelques conventions).

par exemple, vérifiez la documentation Scaladoc pour la liste , et vous verrez certaines des méthodes que vous avez mentionnées ici.

Certaines choses à garder à l'esprit:

  • la plupart du temps , la combinaison A operator+equal B se traduit par A = A operator B , comme dans les exemples ||= ou ++= .

  • les méthodes qui se terminent par : sont associatives, ce qui signifie que A :: B est en fait B.::(A) .

vous trouverez la plupart des réponses par parcourir la documentation Scala. Conserver une référence ici ferait double emploi avec les efforts et prendrait rapidement du retard :)

21
répondu Pablo Fernandez 2013-05-02 17:08:38

vous pouvez les regrouper d'abord selon certains critères. Dans ce post, je vais juste expliquer le caractère de soulignement et la flèche droite.

_._ contient une période. Une période en Scala indique toujours un appel de méthode . Donc à gauche de la période que vous avez le récepteur, et à droite le message (nom de la méthode). Maintenant _ est un symbole spécial en Scala. Il y a plusieurs billets à ce sujet, par exemple cette entrée de blog tous les cas d'utilisation. Ici c'est un fonction anonyme short cut , qui est un raccourci pour une fonction qui prend un argument et appelle la méthode _ . Maintenant _ n'est pas une méthode valide, donc très certainement vous avez vu _._1 ou quelque chose de similaire, c'est-à-dire, invoquer la méthode _._1 sur l'argument de la fonction. _1 à _22 sont les méthodes de tuples qui extraient un particulier élément d'un tuple. Exemple:

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

laisse maintenant supposer un cas d'utilisation pour la fonction raccourci d'application. Donné une carte qui correspond à des entiers à des chaînes de caractères:

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

Wooop, il y a déjà une autre occurrence d'une étrange ponctuation. Le trait d'Union et les caractères plus grands que, qui ressemblent à un flèche droite , est un opérateur qui produit un Tuple2 . Donc, il n'y a pas de différence dans le résultat de l'écriture soit (1, "Eins") ou 1 -> "Eins" , seulement que ce dernier est plus facile à lire, en particulier dans une liste de tuples comme l'exemple de carte. Le -> n'est pas magique, il est, comme quelques autres opérateurs, disponible parce que vous avez tous implicite conversions dans l'objet scala.Predef dans la portée. La conversion qui a lieu ici est

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

ArrowAssoc a le -> méthode qui crée le Tuple2 . Ainsi 1 -> "Eins" est l'appel réel Predef.any2ArrowAssoc(1).->("Eins") . OK. Maintenant, retour à la question originale avec le caractère de soulignement:

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

le soulignement raccourcit ici le code équivalent suivant:

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

notez que la méthode map d'une Map passe dans le tuple de la clé et de la valeur à l'argument de fonction. Comme nous ne sommes intéressés que par les valeurs (les chaînes), nous les extraire avec la méthode _2 sur le tuple.

19
répondu 0__ 2013-06-27 11:26:35

comme un ajout aux réponses brillantes de Daniel et 0__ , Je dois dire que Scala comprend Unicode analogues pour certains des symboles, donc au lieu de

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

on peut écrire

for (n ← 1 to 10) n % 2 match {
  case 0 ⇒ println("even")
  case 1 ⇒ println("odd")
}
14
répondu om-nom-nom 2013-05-02 17:09:44

<= est exactement comme vous le" liriez": "inférieur ou égal". Donc c'est un opérateur mathématique, dans la liste de < (c'est moins que?), > (est plus grand que?), == (égale?), != (n'est pas égal?), <= (est égal ou inférieur?), et >= (est supérieure ou égale?).

cela ne doit pas être confondu avec => qui est une sorte de double main droite Flèche , utilisée pour séparer la liste d'arguments du corps d'une fonction et pour séparer la condition de test dans la correspondance de motif (un case bloc) du corps Exécuté quand une correspondance se produit. Vous pouvez en voir l'exemple dans mes deux réponses précédentes. Tout d'abord, la fonction utiliser:

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

qui est déjà abrégé car les types sont omis. La fonction suivante serait

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

et l'utilisation du motif correspondant:

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"
}
9
répondu 0__ 2013-09-27 17:27:39

Concernant :: il y a un autre Stackoverflow "151990920 entrée" qui couvre les :: . En bref, il est utilisé pour construire Lists par consing " un élément de tête et une liste de queue. C'est à la fois une classe qui représente une liste cons'ED et qui peut être utilisé comme un extracteur, mais le plus souvent c'est une méthode sur une liste. Comme Pablo Fernandez points, puisqu'il se termine par une virgule, c'est droit associatif , ce qui signifie que le récepteur de l'appel de méthode est à droite, et l'argument de la gauche de l'opérateur. De cette façon, vous pouvez exprimer élégamment le consing comme en préprogrammant un nouvel élément de tête à une liste existante:

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

c'est l'équivalent de

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

l'utilisation comme extracteur est la suivante:

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 (33)"
extract(List(2, 3))   // yields "more than one element"

cela ressemble à un opérateur ici, mais c'est vraiment juste une autre (plus lisible) façon d'écrire

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

vous pouvez en savoir plus sur les extracteurs dans ce post .

9
répondu 0__ 2017-05-23 12:10:44

je considère qu'un IDE moderne est critique pour comprendre les grands projets scala. Puisque ces opérateurs sont aussi des méthodes, dans intellij idea je contrôle-click ou contrôle-b dans les définitions.

vous pouvez contrôler-cliquer droit dans un opérateur cons (::) et finir à la Scala javadoc dire" ajoute un élément au début de cette liste."Dans les opérateurs définis par l'utilisateur, cela devient encore plus critique, car ils pourraient être définis dans des implicits difficiles à trouver... votre IDE sait où l'implicite a été défini.

5
répondu Brian 2013-06-27 13:57:44

Scala hérite le plus de les opérateurs arithmétiques de Java . Cela comprend bitwise-ou | (caractère de pipe simple), bitwise-et & , bitwise-exclusive-ou ^ , ainsi que logique (booléen) ou || (deux caractères de pipe) et logique-et && . Il est intéressant de noter que vous pouvez utiliser les opérateurs à caractère unique sur boolean , de sorte que les opérateurs logiques java sont totalement redondants:

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

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

Comme indiqué dans un autre post, les appels se terminant par un signe égal = , sont résolus (si une méthode de même nom n'existe pas!) par une réaffectation:

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

ce "double-check" permet d'échanger facilement un mutable contre une collection immuable:

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)
2
répondu 0__ 2013-09-27 17:28:04

ajoute juste aux autres excellentes réponses. Scala propose deux opérateurs symboliques souvent critiqués, /: ( foldLeft ) et :\ ( foldRight ), le premier étant de droite-associatif. Ainsi, les trois énoncés suivants sont l'équivalent:

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

comme ces trois-là:

( 1 to 100 ).foldRight( 0, _+_ )
( 1 to 100 ).:\( 0 )( _+_ )
( ( 1 to 100 ) :\ 0 )( _+_ )
2
répondu Mr MT 2018-05-02 15:45:50