Scala语法有很多符号。由于使用搜索引擎很难找到这些类型的名字,所以一个全面的列表将会很有帮助。

Scala中所有的符号都是什么,每个符号都有什么作用?

特别地,我想知道 ->, ||=, ++=, <=, _._,::,和:+=。


当前回答

作为Daniel和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")
}

其他回答

我认为现代IDE对于理解大型scala项目至关重要。由于这些操作符也是方法,在intellij思想中,我只是控制-单击或控制-b进入定义。

你可以右键点击一个cons操作符(::),然后在scala javadoc中说“在这个列表的开头添加一个元素”。在用户定义的操作符中,这一点变得更加关键,因为它们可能被定义为很难找到的隐式…你的IDE知道隐式的定义在哪里。

只是为其他精彩的答案锦上添花。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 )( _+_ )

为了便于教学,我将运算符分为四类:

关键字/保留符号 自动导入的方法 常用的方法 语法糖/成分

幸运的是,大多数类别都体现在这个问题中:

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

大多数这些方法的确切含义取决于定义它们的类。例如,<=在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

不过我可能忘了别的意思。

自动导入的方法

因此,如果您在上面的列表中没有找到要查找的符号,那么它一定是一个方法,或者是一个方法的一部分。但是,通常情况下,你会看到一些符号和类的文档没有那个方法。当发生这种情况时,要么您正在查看一个或多个方法与其他方法的组合,要么该方法已导入到作用域,要么通过导入的隐式转换可用。

这些仍然可以在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

在上面的例子中,->是在ArrowAssoc类中通过方法any2ArrowAssoc定义的,该方法接受类型为A的对象,其中A是同一方法的无界类型参数。

常用的方法

因此,许多符号只是类上的方法。例如,如果你有

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

你会在ScalaDoc for List中找到方法++。但是,在搜索方法时必须注意一个约定。以冒号(:)结尾的方法绑定到右边而不是左边。换句话说,上面的方法调用相当于:

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.

Scala和其他语言的一个(在我看来是好的)区别是,它允许你用几乎任何字符来命名你的方法。

您列举的不是“标点符号”,而是简单明了的方法,因此它们的行为因对象而异(尽管存在一些约定)。

例如,查看Scaladoc文档中的List,您将看到这里提到的一些方法。

请记住以下几点:

大多数情况下,A操作符+= B组合转换为A = A操作符B,如||=或++=示例。 以:结尾的方法是右结合的,这意味着A:: B实际上是B.::(A)。

您可以通过浏览Scala文档找到大部分答案。在这里保留一个参考将重复工作,并且它将很快落后:)

你可以先根据某些标准对它们进行分组。在这篇文章中,我将只解释下划线字符和右箭头。

_._ contains a period. A period in Scala always indicates a method call. So left of the period you have the receiver, and right of it the message (method name). Now _ is a special symbol in Scala. There are several posts about it, for example this blog entry all use cases. Here it is an anonymous function short cut, that is it a shortcut for a function that takes one argument and invokes the method _ on it. Now _ is not a valid method, so most certainly you were seeing _._1 or something similar, that is, invoking method _._1 on the function argument. _1 to _22 are the methods of tuples which extract a particular element of a tuple. Example:

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

现在让我们假设函数应用程序快捷方式的用例。给定一个将整数映射到字符串的映射:

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

Wooop, there is already another occurrence of a strange punctuation. The hyphen and greater-than characters, which resemble a right-hand arrow, is an operator which produces a Tuple2. So there is no difference in the outcome of writing either (1, "Eins") or 1 -> "Eins", only that the latter is easier to read, especially in a list of tuples like the map example. The -> is no magic, it is, like a few other operators, available because you have all implicit conversions in object scala.Predef in scope. The conversion which takes place here is

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的map方法将key和value的元组传递给函数参数。由于我们只对值(字符串)感兴趣,所以我们使用元组上的_2方法提取它们。