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

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

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


当前回答

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

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

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

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

其他回答

Regarding :: there is another Stackoverflow entry which covers the :: case. In short, it is used to construct Lists by 'consing' a head element and a tail list. It is both a class which represents a cons'ed list and which can be used as an extractor, but most commonly it is a method on a list. As Pablo Fernandez points out, since it ends in a colon, it is right associative, meaning the receiver of the method call is to the right, and the argument to the left of the operator. That way you can elegantly express the consing as prepending a new head element to an existing list:

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 (1)"
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"
}

你可以在这篇文章中阅读更多关于提取器的信息。

只是为其他精彩的答案锦上添花。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继承了Java的大部分算术运算符。这包括位-或|(单个管道字符)、位-和&、位-异-或^,以及逻辑(boolean)或||(两个管道字符)和逻辑-和&&。有趣的是,你可以在布尔上使用单字符操作符,所以java的逻辑操作符是完全多余的:

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和其他语言的一个(在我看来是好的)区别是,它允许你用几乎任何字符来命名你的方法。

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

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

请记住以下几点:

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

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