List and Tuples in Scala

前端 未结 5 1915
温柔的废话
温柔的废话 2021-01-27 08:33

From the book \'Programming in Scala\' by Martin Odersky:

Another useful container object is the tuple. Like lists, tuples are immutable, but unlike lis

相关标签:
5条回答
  • 2021-01-27 08:48

    Just a note. If you get the Class Name of the List element, somehow Scala knows the type. 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
    
    
    0 讨论(0)
  • 2021-01-27 08:58

    Am I missing something here regarding List and Tuples in 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. 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.

    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. This means that if you want to do something concrete with the one of the elements in the list, i.e. 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:

    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:

    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:

    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:

    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.

    There is a more advanced possibility of using heterogeneous lists using shapeless and it's HList data type:

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

    Which yields:

    2
    
    0 讨论(0)
  • 2021-01-27 09:01

    In very short:

    • In a list, all elements are of the same type (even if it's the all-encompassing Any type).
    • 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 e.g. C background, tuples are like structs, and lists are like arrays.

    0 讨论(0)
  • 2021-01-27 09:11

    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
    
    0 讨论(0)
  • 2021-01-27 09:12

    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.

    No, it can't. The type of your list is List[Any], so all elements are of the same type: Any.

    If you type your code into the Scala REPL, it will tell you at each step what the types are:

    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> :type oneTwoThreee
    List[Any]
    

    Any is a very general, and thus very useless type, since it doesn't have any "interesting" methods. In fact, you are doing pretty much the only thing you can do with an Any: representing it as a 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)”. 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.

    No, it can't. Again let's just ask the Scala REPL what the types are:

    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.

    0 讨论(0)
提交回复
热议问题