What do all of Scala's symbolic operators mean?
scala语法有很多符号。由于使用搜索引擎很难找到这类名称,因此综合列出这些名称会有所帮助。
scala中的所有符号都是什么,它们各自是做什么的?
我特别想了解一下
为了教学目的,我将操作员分为四类:
- 关键字/保留符号
- 自动导入的方法
- 常用方法
- 句法糖/成分
那么,幸运的是,大多数类别都在这个问题中出现了:
1 2 3 4 5 6 7 | -> // 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 |
这些方法的确切含义取决于定义它们的类。例如,
所以,让我们看看。
关键字/保留符号scala中有一些特殊的符号。其中两个被认为是正确的关键字,而其他的只是"保留"。他们是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | // 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)本身。
最后一个,下划线,应该有一个特别的描述,因为它被广泛使用,有很多不同的含义。这是一个示例:
1 2 3 4 5 6 7 8 9 10 11 | 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代码都有三个自动导入:
1 2 3 4 |
前两个仅使类和singleton对象可用。第三个包含所有隐式转换和导入的方法,因为
从
任何其他符号都将通过隐式转换提供。只需看看用
1 | "a" -> 1 // Look for an implicit from String, AnyRef, Any or type parameter |
在上述情况下,
所以,许多符号只是一个类上的方法。例如,如果你这样做
1 | List(1, 2) ++ List(3, 4) |
您可以在scaladoc for list中找到方法
1 | List(1, 2).++(List(3, 4)) |
如果我有,而不是
1 | List(2, 3).::(1) |
因此,在查找以冒号结尾的方法时,需要查看右侧的类型。例如,考虑:
1 | 1 +: List(2, 3) :+ 4 |
第一种方法(
所以,这里有一些可能隐藏方法的句法成分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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 |
最后一个很有趣,因为任何符号方法都可以通过这种方式组合成类似赋值的方法。
当然,代码中可以出现各种组合:
1 2 3 4 | (_+_) // 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和其他语言的一个区别(很好,imo)是它允许您用几乎任何字符命名方法。
您列举的不是"标点符号",而是简单明了的方法,因此它们的行为因对象而异(尽管有一些惯例)。
例如,检查scaladoc文档中的列表,您将看到这里提到的一些方法。
要记住的一些事情:
大多数时候,
A operator+equal B 组合转化为A = A operator B ,就像在||= 或++= 示例中一样。以
: 结尾的方法是右相关的,这意味着A :: B 实际上是B.::(A) 。
您可以通过浏览scala文档找到大多数答案。在这里保存一个参考资料会重复工作,而且会很快落在后面:)
您可以根据一些标准先对它们进行分组。在本文中,我将只解释下划线字符和右箭头。
1 2 3 |
现在让我们假设一个函数应用程序快捷方式的用例。给定一个将整数映射到字符串的映射:
1 |
呜呜,又出现了一个奇怪的标点符号。连字符和大于号字符类似于右手箭头,是一个产生
其中,
1 2 3 | // lets create a sequence from the map by returning the // values in reverse. coll.map(_._2.reverse) // yields List(sniE, iewZ, ierD) |
下面的下划线缩短了以下等效代码:
1 | coll.map(tup => tup._2.reverse) |
注意,map的
除了Daniel和0_uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu
一个人可以写作
这不能与
1 | coll.map(tup => tup._2.reverse) |
它已经被省略为类型。以下函数是
1 2 | // function arguments function body (tup: Tuple2[Int, String]) => tup._2.reverse |
模式匹配使用:
1 2 3 4 5 6 7 8 |
关于
这相当于
1 2 |
使用as提取器对象如下:
1 2 3 4 5 6 7 8 9 |
这看起来像是这里的一个操作符,但它实际上只是另一种(更可读的)书写方式。
1 2 3 4 5 |
您可以在本文中阅读关于提取器的更多信息。
我认为现代的IDE对于理解大型的scala项目是至关重要的。因为这些操作符也是方法,所以在intellij思想中,我只控制click或control-b的定义。
你可以控制点击右键进入一个cons操作符(:)并在scala javadoc结束,说"在这个列表的开头添加一个元素"。在用户定义的操作符中,这变得更为关键,因为它们可以在难以找到的含义中定义…您的IDE知道隐式的定义位置。
只是增加了其他优秀的答案。scala提供了两个经常受到批评的符号运算符:
1 2 3 | ( 1 to 100 ).foldLeft( 0, _+_ ) ( 1 to 100 )./:( 0 )( _+_ ) ( 0 /: ( 1 to 100 ) )( _+_ ) |
这三个是:
1 2 3 | ( 1 to 100 ).foldRight( 0, _+_ ) ( 1 to 100 ).:\( 0 )( _+_ ) ( ( 1 to 100 ) :\ 0 )( _+_ ) |
Scala继承了大部分Java的算术运算符。这包括位或
1 2 3 4 5 |
正如在另一篇文章中指出的,以等号
1 2 |
这种"双重检查"使得可以轻松地将可变集合替换为不可变集合:
1 2 3 4 5 |