【问题标题】:List and Tuples in ScalaScala 中的列表和元组
【发布时间】:2018-06-11 09:36:34
【问题描述】:

来自 Martin Odersky 的《Scala 编程》一书:

另一个有用的容器对象是元组。像列表一样,元组是不可变的, 但与列表不同的是,元组可以包含不同类型的元素。

但我可以:

val oneTwoThreee = List(1, 2, "Third Element") //same as:List.apply(1,2,3)
for (i <- 0 to 2) {
  println(oneTwoThreee.apply((i)))
}

它的输出是:

1 
2
Third Element

所以 Scala 中的 List 可以有不同类型的元素。

来自同一本书:

您可能想知道为什么不能访问元组的元素 就像列表的元素一样,例如“pair(0)”。原因 是列表的 apply 方法总是返回相同的类型,但每个 元组的元素可能是不同的类型:

但如上代码所示,List.apply() 可以返回不同的类型。

我是否在这里遗漏了有关 Scala 中的列表和元组的内容?

【问题讨论】:

    标签: scala list tuples


    【解决方案1】:

    我是否在这里遗漏了有关 Scala 中的列表和元组的内容?

    我认为 Odersky 试图展示的主要观点是每个元组元素都可以包含自己的单独类型,这允许使用多种不同的类型。 List 无法做到的事情,因为列表是同质的,这意味着如果您想要 List[Int],则该列表的所有元素都必须是 Int 值。

    如果查看您创建的列表的类型,您会看到编译器推断出List[Any],这是所有 Scala 类型的通用超类型。这意味着如果你想对列表中的一个元素做一些具体的事情,即它是Int 类型的头元素,你不能因为编译器知道那个元素是它的类型@ 987654327@,您需要了解如何提取底层的“具体”类型:

    scala> val oneTwoThreee = List(1,2,"Third Element")
    oneTwoThreee: List[Any] = List(1, 2, Third Element)
    

    在使用Tuple3[Int, Int, String] 时,实际上“保留”了具体类型:

    scala> val tup = (1, 2, "Third Element")
    tup: (Int, Int, String) = (1,2,Third Element)
    

    现在,如果我们想提取 Int 值之一并将它们加 1,我们可以:

    scala> tup.copy(tup._1 + 1)
    res1: (Int, Int, String) = (2,2,Third Element)
    

    如果我们尝试对 List[Any] 做同样的事情,编译器会理所当然地抱怨:

    scala> oneTwoThreee.head + 1
    <console>:13: error: type mismatch;
     found   : Int(1)
     required: String
           oneTwoThreee.head + 1
                               ^
    

    这个错误有点误导,但这是因为head实际上是Any类型。

    使用shapeless 和它的HList 数据类型可以更高级地使用异构列表:

    import shapeless._
    
    object Tests {
      def main(args: Array[String]): Unit = {
        val hList = 1 :: 2 :: "Third" :: HNil
    
        println(hList.head + 1)
      }
    }
    

    产量:

    2
    

    【讨论】:

      【解决方案2】:

      简而言之:

      • 在一个列表中,所有元素都是相同的类型(即使它是无所不包的 Any 类型)。
      • 在元组中,每个元素都有自己的类型。

      对于列表,您不能要求第一个元素必须是字符串,而第二个元素必须是数字。使用元组可以,编译器会静态检查。

      接下来你可以有一个任意长度的列表,因为所有元素都是相似的,但是元组只能是固定长度,每个元素的类型分别声明。

      如果您来自例如C背景,元组就像结构体,列表就像数组。

      【讨论】:

        【解决方案3】:

        其他答案告诉你什么的一个非常简单的演示。

        val tuplX = (10, "ten")      //tuplX: (Int, String) = (10,ten)
        val listX = List(10, "ten")  //listX: List[Any] = List(10, ten)
        
        tuplX._1 - 6     //res0: Int = 4
        tuplX._2.length  //res1: Int = 3
        
        listX(0) - 6     //Error: value - is not a member of Any
        listX(1).length  //Error: value length is not a member of Any
        

        【讨论】:

          【解决方案4】:

          但我可以:

          val oneTwoThreee = List(1, 2, "Third Element") //same as:List.apply(1,2,3)
          for (i <- 0 to 2) {
            println(oneTwoThreee.apply((i)))
          }
          

          它的输出是:

          1 
          2
          Third Element
          

          所以 Scala 中的 List 可以有不同类型的元素。

          不,它不能。您的列表类型为List[Any],因此所有元素都属于同一类型:Any

          如果您将代码输入到 Scala REPL 中,它会在每一步告诉您类型是什么:

          scala> val oneTwoThreee = List(1, 2, "Third Element") //same as:List.apply(1,2,3)
          oneTwoThreee: List[Any] = List(1, 2, Third Element)
                        ↑↑↑↑↑↑↑↑↑
          

          您也可以随时向 Scala REPL 询问类型:

          scala> :type oneTwoThreee
          List[Any]
          

          Any 是一个非常通用的类型,因此非常无用,因为它没有任何“有趣”的方法。事实上,您正在做的几乎是您可以Any 做的唯一事情:representing it as a String。这就是您没有注意到问题的原因,您不小心碰巧选择了唯一有效的方法

          尝试将列表的第一个元素和第二个元素相乘:

          oneTwoThreee(0) * oneTwoThreee(1)
          // error: value * is not a member of Any
                 oneTwoThreee(0) * oneTwoThreee(1)
                                 ^
          

          您可能想知道为什么不能访问元组的元素 就像列表的元素一样,例如“pair(0)”。原因 是列表的 apply 方法总是返回相同的类型,但每个 元组的元素可能是不同的类型: 但如上代码所示,List.apply() 可以返回不同的类型。

          不,它不能。让我们再次询问 Scala REPL 的类型是什么:

          oneTwoThreee(0)
          //=> res: Any = 1
          //        ↑↑↑
          
          oneTwoThreee(1)
          //=> res: Any = 2
          //        ↑↑↑
          
          oneTwoThreee(2)
          //=> res: Any = Third Element
          //        ↑↑↑
          

          如您所见,类型始终相同:Any

          【讨论】:

            【解决方案5】:

            只是一个注释。如果你得到 List 元素的类名,Scala 就知道它的类型。类型推断可能吗?

            scala> val l = List(1,2,3,"Adriano Avelar")
            val l: List[Any] = List(1, 2, 3, Adriano Avelar)
            
            scala> print(l(3).getClass.getSimpleName)
            String
            
            scala> print(l(2).getClass.getSimpleName)
            Integer
            
            

            【讨论】:

              猜你喜欢
              • 2016-10-08
              • 2016-09-06
              • 2016-11-15
              • 2023-03-19
              • 1970-01-01
              • 1970-01-01
              • 2015-02-13
              • 2012-07-06
              • 2011-05-27
              相关资源
              最近更新 更多