strings loop def scala operators

loop - string*scala



¿Qué significan todos los operadores simbólicos de Scala? (9)

Como una adición a las brillantes respuestas de Daniel y 0__, debo decir que Scala entiende los análogos de Unicode para algunos de los símbolos, así que en lugar de

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

uno puede escribir

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

La sintaxis de Scala tiene muchos símbolos. Dado que es difícil encontrar este tipo de nombres utilizando motores de búsqueda, una lista completa de ellos sería útil.

¿Cuáles son todos los símbolos en Scala y qué hace cada uno de ellos?

En particular, me gustaría saber sobre -> , ||= , ++= , <= , _._ , :: y :+= .


Con respecto a :: hay otra entrada de que cubre el :: caso. En resumen, se utiliza para construir Lists " consing " un elemento principal y una lista de cola. Es a la vez una class que representa una lista de candidatos y que se puede utilizar como un extractor, pero más comúnmente es un método en una lista. Como señala Pablo Fernández, como termina en dos puntos, es asociativo a la derecha , lo que significa que el receptor de la llamada al método está a la derecha y el argumento a la izquierda del operador. De esa manera, puede expresar con elegancia la consideración como anteponer un nuevo elemento de cabecera a una lista existente:

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

Esto es equivalente a

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

El uso como objeto extractor es el siguiente:

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"

Esto parece un operador aquí, pero en realidad es solo otra forma (más legible) de escribir

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

Puedes leer más sobre los extractores en esta publicación .


Considero que un IDE moderno es crítico para entender proyectos de gran escala. Dado que estos operadores también son métodos, en la idea más inteligente simplemente controlo clic o control-b en las definiciones.

Puede hacer clic con el botón derecho sobre un operador de contras (: :) y terminar en el scala javadoc que dice "Agrega un elemento al principio de esta lista". En operadores definidos por el usuario, esto se vuelve aún más crítico, ya que podrían definirse en implicaciones difíciles de encontrar ... su IDE sabe dónde se definió lo implícito.


Divido a los operadores, con el propósito de enseñar, en cuatro categorías :

  • Palabras clave / símbolos reservados
  • Métodos importados automáticamente
  • Métodos comunes
  • Azúcares sintácticos / composición

Es una suerte, entonces, que la mayoría de las categorías estén representadas en la pregunta:

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

El significado exacto de la mayoría de estos métodos depende de la clase que los define. Por ejemplo, <= en Int significa "menor o igual que" . El primero, -> , voy a dar como ejemplo a continuación. :: es probablemente el método definido en la List (aunque podría ser el objeto del mismo nombre), y :+= es probablemente el método definido en varias clases de Buffer .

Entonces, vamos a verlos.

Palabras clave / símbolos reservados

Hay algunos símbolos en Scala que son especiales. Dos de ellas se consideran palabras clave adecuadas, mientras que otras son simplemente "reservadas". Son:

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

Todos estos son parte del idioma y, como tal, se pueden encontrar en cualquier texto que describa correctamente el idioma, como la especificación Scala (PDF) en sí.

El último, el guión bajo, merece una descripción especial, porque se usa mucho y tiene muchos significados diferentes. Aquí hay una muestra:

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

Aunque probablemente olvidé algún otro significado.

Métodos importados automáticamente

Entonces, si no encontró el símbolo que está buscando en la lista anterior, debe ser un método o parte de uno. Pero, a menudo, verá algún símbolo y la documentación de la clase no tendrá ese método. Cuando esto sucede, o está mirando la composición de uno o más métodos con otra cosa, o el método se ha importado en el alcance, o está disponible a través de una conversión implícita importada.

Todavía se pueden encontrar en ScalaDoc : solo tienes que saber dónde buscarlos. O, en su defecto, mira el index (actualmente roto en 2.9.1, pero disponible en todas las noches).

Cada código de Scala tiene tres importaciones automáticas:

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

Los dos primeros solo hacen disponibles clases y objetos singleton. El tercero contiene todas las conversiones implícitas y métodos importados, ya que Predef es un objeto en sí mismo.

Mirando adentro, Predef muestra rápidamente algunos símbolos:

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

Cualquier otro símbolo estará disponible a través de una conversión implícita . Solo mire los métodos etiquetados con implicit que reciben, como parámetro, un objeto de tipo que está recibiendo el método. Por ejemplo:

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

En el caso anterior, -> se define en la clase ArrowAssoc través del método any2ArrowAssoc que toma un objeto de tipo A , donde A es un parámetro de tipo ilimitado para el mismo método.

Métodos comunes

Entonces, muchos símbolos son simplemente métodos en una clase. Por ejemplo, si lo haces

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

Encontrará el método ++ directamente en ScalaDoc for List . Sin embargo, hay una convención que debe tener en cuenta al buscar métodos. Los métodos que terminan en dos puntos (:) se unen a la derecha en lugar de a la izquierda. En otras palabras, mientras que la llamada al método anterior es equivalente a:

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

Si tuviera, en lugar de 1 :: List(2, 3) , eso sería equivalente a:

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

Por lo tanto, debe buscar el tipo que se encuentra a la derecha cuando busque métodos que terminen en dos puntos. Considere, por ejemplo:

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

El primer método ( +: :) se une a la derecha y se encuentra en la List . El segundo método ( :+ ) es solo un método normal, y se une a la izquierda - nuevamente, en la List .

Azúcares sintácticos / composición

Entonces, aquí hay algunos azúcares sintácticos que pueden ocultar un método:

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

El último es interesante, porque cualquier método simbólico puede combinarse para formar un método similar a una asignación de esa manera.

Y, por supuesto, hay varias combinaciones que pueden aparecer en el código:

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


Puedes agrupar los primeros según algunos criterios. En este post solo explicaré el carácter de subrayado y la flecha derecha.

_._ contiene un punto. Un período en Scala siempre indica una llamada de método . Así que a la izquierda del período que tiene el receptor, y a la derecha del mensaje (nombre del método). Ahora _ es un símbolo especial en Scala. Hay varias publicaciones al respecto, por ejemplo, en esta entrada de blog todos los casos de uso. Aquí es un atajo de función anónima , es un atajo para una función que toma un argumento e invoca el método _ en él. Ahora _ no es un método válido, por lo que seguramente viste _._1 o algo similar, es decir, invocar el método _._1 en el argumento de la función. _1 a _22 son los métodos de las tuplas que extraen un elemento particular de una tupla. Ejemplo:

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

Ahora asumamos un caso de uso para el acceso directo de la aplicación de función. Dado un mapa que mapea enteros a cadenas:

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

Wooop, ya hay otra ocurrencia de una puntuación extraña. El guión y los caracteres mayores que, que se asemejan a una flecha hacia la derecha , es un operador que produce un Tuple2 . Entonces, no hay diferencia en el resultado de la escritura (1, "Eins") o 1 -> "Eins" , solo que este último es más fácil de leer, especialmente en una lista de tuplas como el ejemplo de mapa. El -> no es magia, está disponible, como algunos otros operadores, porque tiene todas las conversiones implícitas en el objeto Predef en el alcance. La conversión que tiene lugar aquí es

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

Donde ArrowAssoc tiene el método -> que crea el Tuple2 . Así 1 -> "Eins" es real la llamada Predef.any2ArrowAssoc(1).->("Eins") . De acuerdo. Ahora volvamos a la pregunta original con el carácter de subrayado:

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

El subrayado aquí acorta el siguiente código equivalente:

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

Tenga en cuenta que el método de map de un Mapa pasa en la tupla de clave y valor al argumento de la función. Como solo nos interesan los valores (las cadenas), los extraemos con el método _2 en la tupla.


Scala hereda la mayoría de los operadores aritméticos de Java . Esto incluye bitwise o | (carácter de una sola tubería), bitwise-y & , bitwise-exclusive-o ^ , así como lógico (booleano) o || (dos caracteres de tubería) y lógicos y && . Curiosamente, puede usar los operadores de un solo carácter en boolean , por lo que los operadores lógicos de Java son totalmente redundantes:

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

Como se señaló en otra publicación, las llamadas que terminan en un signo igual = , se resuelven (¡si un método con ese nombre no existe!) Mediante una reasignación:

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

Este ''doble control'' hace posible, intercambiar fácilmente un mutable por una colección inmutable:

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)


Solo añadiendo a las otras excelentes respuestas. Scala ofrece dos operadores simbólicos a menudo criticados, los operadores /: ( foldLeft ) y :/ ( foldRight ), el primero es asociativo por derecho. Así que las siguientes tres afirmaciones son equivalentes:

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

Como son estos tres:

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


Una diferencia (buena, IMO) entre Scala y otros idiomas es que le permite nombrar sus métodos con casi cualquier carácter.

Lo que enumera no es "puntuación" sino métodos sencillos y, como tal, su comportamiento varía de un objeto a otro (aunque existen algunas convenciones).

Por ejemplo, revise la documentación de Scaladoc para ver la Lista , y verá algunos de los métodos que mencionó aquí.

Algunas cosas para tener en mente:

  • La mayoría de las veces la combinación del A operator+equal B traduce en el A = A operator B , como en los ejemplos ||= o ++= .

  • Los métodos que terminan en : son asociativos correctos, esto significa que A :: B es en realidad A :: B ( B.::(A) .

Encontrará la mayoría de las respuestas en la documentación de Scala. Mantener una referencia aquí duplicaría los esfuerzos, y se retrasaría rápidamente :)


<= es como lo "leerías": ''menor que o igual a''. Entonces, es un operador matemático, en la lista de < (es menor que?), > (Es mayor que?), == (¿es igual a?) != (No es igual?), <= (Es menor o igual que ?), y >= (es mayor o igual que?).

Esto no debe confundirse con => que es una especie de doble flecha hacia la derecha , utilizada para separar la lista de argumentos del cuerpo de una función y para separar la condición de prueba en la coincidencia de patrones (un bloque de case ) del cuerpo ejecutado cuando se produce una coincidencia. Puedes ver un ejemplo de esto en mis dos respuestas anteriores. En primer lugar, la función utiliza:

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

que ya está abreviado ya que los tipos se omiten. La siguiente función sería

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

y el uso de coincidencia de patrones:

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