Scala列表中的最后一个元素是什么?

4
在Scala中,List是以链表的形式实现的。它有两个部分:头部和尾部。列表中的最后一个单元格包含Nil。
在Scala中,Nil本身是一个单例并扩展了List[Nothing],正如这里所记录的。
由于Nil是一个单例,这是否意味着Scala中所有List实例的末尾元素都具有相同的对象?

1
在这里发布:https://users.scala-lang.org/t/a-question-on-scala-list/2519 - Mandroid
2个回答

11

是和不是。

实际上,尽管结束标记总是单例的 Nil 对象,但最后一个元素是紧挨着它前面的那个。

scala> val a = 1 :: 2 :: Nil
a: List[Int] = List(1, 2)

scala> a.last
res10: Int = 2

你可能会对术语提出异议,但编码者在这方面通常是实证主义者,事实是,代码所说的就是真相。


这意味着在内存层面上,Nil 确实是所有列表实例的最后一个元素,但从程序员的角度来看,Scala 告诉我们最后一个元素是倒数第二个元素,因为程序员只关心到那个元素。 - Mandroid
1
我更倾向于说,Nil不是列表的一个元素,而是一个标记,表示列表已经结束。由于List是抽象的,谁知道特定的列表是如何实现和分配在内存中的?既然我们说Nil是空列表,从这个角度来看,说Nil是该列表的一个元素是不一致的。空列表的大小也是0。 - user unknown
我认为您可能不会对术语进行争论。列表是封闭的,你只得到::Nil。最后一个::有一个尾部是Nil; 它的头是最后一个元素。 - som-snytt

2

是的,所有的列表都以空列表Nil结尾。

您可以通过尝试创建一个没有Nil结尾的列表来证明这一点。

val a = 1 :: 2 // 失败

val a = 1 :: Nil // 成功

val a = scala.collection.immutable.::(1, Nil) // 成功

最后一种情况调用了::的案例类构造函数,该构造函数扩展了List,因此创建了一个List::案例类的代码是...

final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
  override def tail : List[B] = tl
  override def isEmpty: Boolean = false
}

操作符::定义在List对象中,当你执行像1 :: Nil这样的操作时,它会调用case类::为你创建一个列表。

如下所示:

  def ::[B >: A] (x: B): List[B] =
    new scala.collection.immutable.::(x, this)

你可以看到,在 List 上执行 map 操作的代码会迭代 List 直到达到 Nil。
来自 List 类中的 map 函数片段。
while (rest ne Nil) {
          val nx = new ::(f(rest.head), Nil)
          t.tl = nx
          t = nx
          rest = rest.tail
        }

(ne 代表不等于)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接