Scala中的列表和元组

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala中的列表和元组相关的知识,希望对你有一定的参考价值。

来自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中List和Tuples的内容吗?

答案

我在这里错过了关于Scala中List和Tuples的内容吗?

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

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

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
另一答案

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

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
另一答案

很简短:

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

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

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

如果你来自例如C背景,元组就像结构一样,列表就像数组一样。

另一答案

但我可以:

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

以上是关于Scala中的列表和元组的主要内容,如果未能解决你的问题,请参考以下文章

Scala从入门到精通之四-映射和元组

scala 基础六 scala Map和元组的操作

「大数据」(八十九)Scala之映射和元组

Scala的映射和元组操作

python中列表和元组之间有啥区别?

python中的列表和元组