首页 文章

Scala的所有符号运算符都意味着什么?

提问于
浏览
370

Scala语法有很多符号 . 由于使用搜索引擎很难找到这些类型的名称,因此全面列出这些名称会很有帮助 .

Scala中的所有符号都是什么,它们各自做了什么?

特别是,我想了解 ->||= ,_ ++=<=_._:::+= .

9 回答

  • 9

    Scala和其他语言之间的一个(好的,IMO)区别在于它允许您使用几乎任何字符命名您的方法 .

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

    例如,检查Scaladoc documentation for List,您将看到此处提到的一些方法 .

    要注意的一些事项:

    • A operator+equal B 组合的大部分时间都转换为 A = A operator B ,就像在 ||=++= 示例中一样 .

    • : 结尾的方法是右关联的,这意味着 A :: B 实际上是 B.::(A) .

    您可以通过浏览Scala文档找到大多数答案 . 在这里保留一个参考将重复努力,它会很快落后:)

  • 19

    Scala继承了Java's arithmetic operators的大部分内容 . 这包括按位 - 或 | (单管道字符),按位 - 和 & ,按位 - 异或 ^ ,以及逻辑(布尔)或 || (两个管道字符)和逻辑 - 和 && . 有趣的是,您可以在 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`
    

    这种“双重检查”使得为不可变集合轻松交换mutable成为可能:

    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)
    
  • 9

    关于 :: ,还有另一个Stackoverflow条目涵盖 :: 案例 . 简而言之,它用于构造 Lists 由' consing '构成头元素和尾部列表 . 它既是一个class,代表一个cons'ed列表,可以用作提取器,但最常见的是它是一个列表上的方法 . 正如Pablo Fernandez指出的那样,因为它以冒号结束,所以它是 right associative ,意味着方法调用的接收器在右边,而运算符左边的参数 . 通过这种方式,您可以优雅地表达内容,将新头元素添加到现有列表中:

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

    您可以在this post中阅读有关提取器的更多信息 .

  • 2

    为了教学的目的,我把操作员划分为 four categories

    • 关键字/保留符号

    • 自动导入的方法

    • 常用方法

    • 句法糖/组成

    幸运的是,大多数类别都在问题中表示:

    ->    // 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 表示"less than or equal to" . 第一个, -> ,我将在下面举例说明 . :: 可能是在 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 Specification(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找到:你只需要知道在哪里寻找它们 . 或者,如果不这样做,请查看index(目前已打破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上找到 ++ 的方法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.
    
  • 3

    <= 就像你会"read"它:'less than or equals' . 所以它是一个数学运算符,在 < (小于?), > (大于?), == (等于?), != (不等于?),_ <= (小于或等于?)的列表中,和 >= (大于或等于?) .

    这不能与 => 混淆, => 是一种 double right-hand arrow ,用于将参数列表与函数体分开,并将模式匹配( case 块)中的测试条件与匹配发生时执行的主体分开 . 你可以在我之前的两个答案中看到这个例子 . 一,功能用途:

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

    只是添加其他优秀的答案 . 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 )( _+_ )
    
  • 5

    作为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")
    }
    
  • 22

    我认为现代IDE对于理解大型scala项目至关重要 . 由于这些运算符也是方法,因此我只需按控制点击或控制b进入定义 .

    你可以控制点击右边的一个cons运算符(::)并最终在scala javadoc上说“在这个列表的开头添加一个元素” . 在用户定义的运算符中,这变得更加关键,因为它们可以在难以发现的含义中定义...您的IDE知道隐式定义的位置 .

  • 14

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

    _._ 包含一段时间 . Scala中的句点始终表示 method call . 所以你有接收器的那个时期的左边,右边是消息(方法名称) . 现在 _ 在Scala中是 special symbol . 有几篇关于它的帖子,例如this blog entry所有用例 . 这是一个 anonymous function 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")
    

    Wooop,已经出现了另一个奇怪的标点符号 . 连字符和大于字符,类似于 right-hand arrow ,是一个产生Tuple2的运算符 . 因此,编写 (1, "Eins")1 -> "Eins" 的结果没有区别,只是后者更容易阅读,特别是在像 Map 示例这样的元组列表中 . -> 并不神奇,它可以像其他几个运算符一样可用,因为你在范围内的对象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的 map 方法将key和value的元组传递给function参数 . 由于我们只对值(字符串)感兴趣,因此我们使用元组上的 _2 方法提取它们 .

相关问题