简体   繁体   English

Scala 中的列表和元组

[英]List and Tuples in Scala

From the book 'Programming in Scala' by Martin Odersky:来自 Martin Odersky 所著的“Scala 编程”一书:

Another useful container object is the tuple.另一个有用的容器对象是元组。 Like lists, tuples are immutable, but unlike lists, tuples can contain different types of elements.与列表一样,元组是不可变的,但与列表不同的是,元组可以包含不同类型的元素。

But I can have:但我可以:

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

And its output is:它的输出是:

1 
2
Third Element

So List in Scala can have different types of elements.所以 Scala 中的 List 可以有不同类型的元素。

And from same book:来自同一本书:

You may be wondering why you can't access the elements of a tuple like the elements of a list, for example, with “pair(0)”.您可能想知道为什么不能像列表中的元素一样访问元组的元素,例如,使用“pair(0)”。 The reason is that a list's apply method always returns the same type, but each element of a tuple may be a different type:原因是列表的 apply 方法总是返回相同的类型,但元组的每个元素可能是不同的类型:

But as above code shows, List.apply() can return different types.但是如上代码所示, List.apply() 可以返回不同的类型。

Am I missing something here regarding List and Tuples in Scala?我在这里遗漏了有关 Scala 中的列表和元组的内容吗?

Am I missing something here regarding List and Tuples in Scala?我在这里遗漏了有关 Scala 中的列表和元组的内容吗?

I think the main point Odersky is trying to show is that each tuple element can contain its own individual type, which allows using multiple different types.我认为 Odersky 试图表明的主要观点是每个元组元素都可以包含自己的单独类型,这允许使用多种不同的类型。 Something that a List can't do because a list is homogeneous, meaning if you want a List[Int] , all elements of that list must be Int values. List不能做的事情,因为一个列表是同构的,这意味着如果你想要一个List[Int] ,该列表的所有元素都必须是Int值。

If you look at the type of the list you created, you'll see that the compiler infers List[Any] , which is the common supertype of all Scala types.如果查看您创建的列表的类型,您会看到编译器推断List[Any] ,它是所有 Scala 类型的通用超类型。 This means that if you want to do something concrete with the one of the elements in the list, ie it's head element which is of type Int , you can't because all the compiler knows about that element is that its of type Any , and you'll need to some how extract the underlying "concrete" type:这意味着如果你想对列表中的元素之一做一些具体的事情,即它是Int类型的 head 元素,你不能,因为所有编译器都知道该元素是它的类型Any ,并且您需要了解如何提取底层的“具体”类型:

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

While using a Tuple3[Int, Int, String] , actually "keeps" the concrete types:在使用Tuple3[Int, Int, String] ,实际上“保留”了具体类型:

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

Now if we want to extract one of the Int values and increment them by 1, we can:现在,如果我们想提取一个Int值并将它们加 1,我们可以:

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

If we tried doing the same with a List[Any] , the compiler would rightfully complain:如果我们尝试对List[Any]做同样的事情,编译器会理所当然地抱怨:

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

The error is somewhat misleading, but this happens due to the fact head is actually of type Any .这个错误有点误导,但发生这种情况是因为head实际上是Any类型。

There is a more advanced possibility of using heterogeneous lists using shapeless and it's HList data type:有使用非均相列表使用更先进的可能性无形的,它是HList数据类型:

import shapeless._

object Tests {
  def main(args: Array[String]): Unit = {
    val hList = 1 :: 2 :: "Third" :: HNil

    println(hList.head + 1)
  }
}

Which yields:其中产生:

2

A very simple demonstration of what the other answers are telling you.其他答案告诉您的内容的非常简单的演示。

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

In very short:简而言之:

  • In a list, all elements are of the same type (even if it's the all-encompassing Any type).在列表中,所有元素都属于同一类型(即使它是包罗万象的 Any 类型)。
  • In a tuple, each element has its own type.在元组中,每个元素都有自己的类型。

With a list, you cannot require that the first element must be a string, and the second, a number.对于列表,您不能要求第一个元素必须是字符串,第二个元素必须是数字。 With a tuple, you can, and the compiler will statically check that.使用元组,您可以,并且编译器将静态检查它。

What follows is that you can have a list of an arbitrary length, because all elements are alike, but a tuple can be only of a fixed length, with the type of each element declared separately.接下来是你可以有一个任意长度的列表,因为所有元素都是相似的,但是元组只能是固定长度的,每个元素的类型单独声明。

If you come from eg C background, tuples are like structs, and lists are like arrays.如果你来自例如 C 背景,元组就像结构,列表就像数组。

But I can have:但我可以:

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

And its output is:它的输出是:

 1 2 Third Element

So List in Scala can have different types of elements.所以 Scala 中的 List 可以有不同类型的元素。

No, it can't.不,不能。 The type of your list is List[Any] , so all elements are of the same type: Any .您的列表的类型是List[Any] ,因此所有元素的类型都相同: Any

If you type your code into the Scala REPL, it will tell you at each step what the types are:如果你在 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)
              ↑↑↑↑↑↑↑↑↑

You can also always ask the Scala REPL for the type:您也可以随时向 Scala REPL 询问类型:

scala> :type oneTwoThreee
List[Any]

Any is a very general, and thus very useless type, since it doesn't have any "interesting" methods. Any是一个非常通用的类型,因此非常无用,因为它没有任何“有趣”的方法。 In fact, you are doing pretty much the only thing you can do with an Any : representing it as a String .事实上,您几乎可以Any做唯一的事情:将其表示为String That's why you're not noticing the problem, you accidentally happened to pick the only thing that works.这就是为什么你没有注意到这个问题,你不小心选择了唯一有效的方法。

Try multiplying the first and second element of your list:尝试将列表的第一个和第二个元素相乘:

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

You may be wondering why you can't access the elements of a tuple like the elements of a list, for example, with “pair(0)”.您可能想知道为什么不能像列表中的元素一样访问元组的元素,例如,使用“pair(0)”。 The reason is that a list's apply method always returns the same type, but each element of a tuple may be a different type: But as above code shows, List.apply() can return different types.原因是列表的 apply 方法总是返回相同的类型,但元组的每个元素可能是不同的类型:但如上代码所示, List.apply() 可以返回不同的类型。

No, it can't.不,不能。 Again let's just ask the Scala REPL what the types are:再次让我们询问 Scala REPL 的类型是什么:

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

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

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

As you can see, the type is always the same: Any .如您所见,类型始终相同: Any

Just a note.只是一个注释。 If you get the Class Name of the List element, somehow Scala knows the type.如果你得到 List 元素的类名,Scala 就会知道它的类型。 Type inference maybe?类型推断可能吗?

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM