Что означают все символические операторы Scala?



синтаксис Scala имеет много символов. Поскольку эти типы имен трудно найти с помощью поисковых систем, полный список из них было бы полезно.



каковы все символы в Scala, и что делает каждый из них?



в частности, я хотел бы знать, о ->,||=,++=,<=,_._,:: и :+=.

1102   9  

9 ответов:

я разделяю операторов, с целью обучения, на четыре категории:

  • ключевые слова/зарезервированные символы
  • автоматически импортируемые методы
  • общие методы
  • синтаксический сахар/состав

хорошо, что большинство категорий представлены в вопросе:

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

точное значение большинства из этих методов зависит от класса, определяя их. Например, <= on Int означает "меньше или равно". Первый, ->, я приведу в качестве примера ниже. :: вероятно, это метод, определенный на List (хотя может быть объект с тем же именем), и :+= вероятно, это метод, определенный на различных Buffer классы.

Итак, давайте посмотрим их.

ключевые слова/зарезервированные символы

есть некоторые символы в Scala, которые специальный. Два из них считаются правильными ключевыми словами, в то время как другие просто "зарезервированы". Они таковы:

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

это все часть языка, и, как таковой, можно найти в любом тексте, который правильно описывает язык, например Спецификация Scala(формат PDF) сам.

последнее, подчеркивание, заслуживает особого описания, потому что оно так широко используется и имеет так много разных значений. Вот пример:

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

я, наверное, забыл какой-то другой смысл, хотя.

автоматически импортируемые методы

Итак, если вы не нашли символ, который вы ищете в списке выше, то это должен быть метод или часть одного. Но, часто, вы увидите какой-то символ и документация для класса не будет иметь этот метод. Когда это происходит, либо вы смотрите на композицию одного или нескольких методов с чем-то другим, либо метод был импортируется в область видимости или доступен через импортированное неявное преобразование.

эти еще можно найти on ScalaDoc: вы просто должны знать, где их искать. Или, в противном случае, посмотрите на индекс (в настоящее время сломан на 2.9.1, но доступен на ночь).

каждый код Scala имеет три автоматических импорта:

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

первые два делают доступными только классы и одноэлементные объекты. Третий содержит все неявные преобразования и импортированные методы, так как Predef это сам объект.

заглянув внутрь Predef быстро показать некоторые символы:

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

любой другой символ будет доступен через неявное преобразование. Просто посмотрите на методы, помеченные implicit что получать в качестве параметра объект типа, который получает метод. Например:

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

в приведенном выше случае, -> is определено в классе ArrowAssoc методом any2ArrowAssoc что принимает объект типа A, где A является неограниченным параметром типа для того же метода.

общие методы

Итак, многие символы-это просто методы в классе. Например, если вы делаете

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

вы найдете способ ++ прямо на ScalaDoc для список. Тем не менее, есть одно соглашение, которое вы должны знать при поиске методы. Методы, заканчивающиеся двоеточием (:) привязать справа вместо левой. Другими словами, в то время как приведенный выше вызов метода эквивалентен:

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

если бы я имел, вместо 1 :: List(2, 3), что было бы эквивалентно:

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

поэтому вам нужно посмотреть на найденный тип справа при поиске методов, заканчивающихся двоеточием. Рассмотрим, например:

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

первый метод (+:) привязывается справа, а находится на List. Второй способ (:+) - это просто обычный метод, и привязывается слева-опять же, на List.

синтаксический сахар/состав

Итак, вот несколько синтаксических сахаров, которые могут скрывать метод:

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

последнее интересно, потому что любой символьный метод может быть объединен для формирования метода, подобного присваиванию.

и, конечно, есть различные комбинации, которые могут появиться в код:

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

одна (хорошая, IMO) разница между Scala и другими языками заключается в том, что она позволяет вам называть ваши методы почти любым символом.

то, что вы перечисляете, - это не "пунктуация", а простые и простые методы, и поэтому их поведение варьируется от одного объекта к другому (хотя есть некоторые соглашения).

например, регистрация Scaladoc документация для List, и вы увидите некоторые из методов, которые вы упомянули здесь.

некоторые вещи имейте в виду:

  • в большинстве случаев A operator+equal B сочетание переводится как A = A operator B, как в ||= или ++= примеры.

  • методы, которые заканчиваются на : являются правильными ассоциативными, это означает, что A :: B на самом деле B.::(A).

вы найдете большинство ответов, просмотрев документацию Scala. Сохранение ссылки здесь будет дублировать усилия, и она быстро отстает:)

вы можете сгруппировать их в первую очередь в соответствии с некоторыми критериями. В этом посте я просто объясню символ подчеркивания и стрелку вправо.

_._ содержит период. Точка в Scala всегда указывает на вызов метода. Таким образом, слева от периода у вас есть получатель, а справа от него сообщение (имя метода). Сейчас _ это специальный символ в Scala. Есть несколько сообщений об этом, например эта запись в блоге все использовать случаи. Вот это анонимная функция short cut, что это ярлык для функции, которая принимает один аргумент и вызывает метод _ на нем. Сейчас _ не является допустимым методом, так что, безусловно, вы видели _._1 или что-то подобное, то есть вызов метода _._1 на аргумент функции. _1 до _22 являются методы кортежей, которые извлекают определенный элемент кортежа. Пример:

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

теперь давайте предположим вариант использования для функция ярлык приложения. Дано отображение, которое отображает целые числа в строки:

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

ВООП, есть уже другое появление странной пунктуации. Дефис и больше-чем символы, которые напоминают стрелка вправо, является оператором, который производит Tuple2. Так что нет никакой разницы в результате написания либо (1, "Eins") или 1 -> "Eins", только что последний легче читать, особенно в списке кортежей, как на карте образец. Элемент -> - это не магия, это, как и несколько других операторов, у нас все подразумевается преобразования в объекте scala.Predef в область. Преобразование, которое происходит здесь

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

здесь ArrowAssoc имеет -> метод, который создает Tuple2. Таким образом 1 -> "Eins" актуален вызов Predef.any2ArrowAssoc(1).->("Eins"). Ладно. Теперь вернемся к исходному вопросу с символом подчеркивания:

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

подчеркивание здесь сокращает следующий эквивалентный код:

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

отметим, что map метод карты передает кортеж ключа и значения аргументу функции. Поскольку нас интересуют только значения (строки), мы извлекаем их с помощью _2 метод на кортеже.

в дополнение к блестящим ответам Даниила и 0__, я должен сказать, что Scala понимает Unicode аналоги для некоторых символов, так вместо

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

можно писать

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

<= так же, как вы бы "прочитали" его: "меньше или равно". Так что это математический оператор, в списке < (меньше?),> (больше?),== (равных?),!= (Не равно?),<= (меньше или равно?), и >= (больше или равно?).

это не должно быть перепутал С => что-то вроде двойная стрелка вправо, используется для отделения списка аргументов от тела a функция и отделить условие испытаний в сопоставлении с образцом (a case block)из тела, выполненного при совпадении. Вы можете увидеть пример этого в моих предыдущих двух ответах. Во-первых, использование функции:

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

который уже сокращен, поскольку типы опущены. Следующая функция будет

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

и использование сопоставления с образцом:

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

о :: есть еще Stackoverflow запись, которая охватывает :: случае. Короче говоря, он используется для построения Lists by'consing' головной элемент и хвостовой список. Это как класс который представляет собой список минусов и который может быть использован в качестве экстрактора, но чаще всего это метод on список. Как указывает Пабло Фернандес, поскольку он заканчивается двоеточием, это право ассоциативный, что означает, что получатель вызова метода находится справа, а аргумент-слева от оператора. Таким образом, вы можете элегантно выразить консинг как перед новый головной элемент в существующий список:

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

это эквивалентно

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

использование в качестве объекта экстрактора выглядит следующим образом:

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"

это выглядит как оператор здесь, но это действительно просто еще один (более читаемый) способ пишу

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

вы можете прочитать больше о экстракторах в этот пост.

Я считаю, что современная IDE имеет решающее значение для понимания крупных проектов scala. Поскольку эти операторы также являются методами, в intellij idea я просто control-click или control-b В определения.

вы можете управлять-щелкните правой кнопкой мыши в оператор минусов (::) и в конечном итоге в scala javadoc говоря "добавляет элемент в начале этого списка."В определяемые пользователем операторы, это становится еще более важным, поскольку они могут быть определены в трудно найти неявные преобразования... интерфейс IDE знает где неявное было определено.

Scala наследует большую часть арифметические операторы Java. Это включает в себя побитовое или | (один символ), побитовое и &, побитовое исключающее-ИЛИ ^, а также логические (булевы) или || (два символа трубы) и логический-и &&. Интересно, что вы можете использовать односимвольные операторы на boolean, так что Ява иш логических операторов являются абсолютно лишними:

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

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

как указано в другом сообщении, вызовы заканчиваются равными знак =, решаются (если метод с таким именем не существует!) путем переназначения:

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

эта "двойная проверка" позволяет легко обменять изменяемую коллекцию на неизменяемую:

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)

просто добавляя к другим отличным ответам. Scala предлагает два часто критикуемых символьных оператора,/: (foldLeft) и :\ (foldRight) операторы, первый из которых является правоассоциативным. Таким образом, следующие три утверждения эквивалентны:

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

как и эти три:

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

Comments

    Ничего не найдено.