列表上的Scala抽象值成员返回意外结果?

时间:2014-05-10 15:40:57

标签: scala arity

我不确定这是不是一个错误,或者我不太了解Scala。今天我在REPL中玩了一些列表功能。这是我做的:

首先,我创建了一个列表:

scala> val myList = List(1.0, 2.0, 3.0)
myList: List[Double] = List(1.0, 2.0, 3.0)

接下来,我通过添加另一个双精度创建了另一个列表:

scala> val newMyList = 4.0 :: myList
newMyList: List[Double] = List(4.0, 1.0, 2.0, 3.0)

现在,当我在newMyList上请求productArity(List.productArity)时:

scala> print(newMyList.productArity)
2

它似乎仍然以不同于其他列表的方式处理第一个列表。这是一个预期的行为还是一个错误?

scala> print(newMyList.productElement(0))
4.0
scala> print(newMyList.productElement(1))
List(1.0, 2.0, 3.0)

注意,我在尝试访问高于0的元素时得到java.lang.IndexOutOfBoundsException,1应该返回2.0,3应该返回3.0,对吗?

2 个答案:

答案 0 :(得分:5)

Scala中的List是基于cons-cell的结构,类似于LISP语言中使用的列表:它由每个都有头元素和尾元素的单元组成,其中最后一个单元有一个Nil的尾元素。

Scala中的空单元格为Nil,非空单元格为::(又名“Cons”)。这两个具体的列表子类型实现为案例类,提供您引用的Product特征。

所以而不是

List(1.0, 2.0, 3.0)

你可以想到

::(1.0, ::(2.0, ::(3.0, Nil)))

或以图解方式解释

Cons(1.0, .)
          Cons(2.0, .)
                    Cons(3.0, .)
                              Nil

::是arity 2的产物,第一个元素是头部,第二个元素是尾部。这就是为什么您将4List(1, 2, 3)作为第二个列表中的两个产品元素的原因。

要访问列表中的元素,请改用apply。列表大小由size

给出
List(4.0, 1.0, 2.0, 3.0).apply(2) // -> 2.0
List(4.0, 1.0, 2.0, 3.0).size     // -> 4

答案 1 :(得分:2)

List - Nil::有两种可能的情况。

::定义如下:

case class ::[A](val head: A, val tail: List[A]) extends List[A]

所以它是一个有两个元素的产品,列表的头部和尾部。

这就是为什么非空列表的productArity返回2,因为您正在调用::.productArity

相反,Nil.productArity返回0.

newMyList.productElement(0)是列表的头部,因为您从::获取第一个元素,而newMyList.productElement(1)是列表的尾部。 Cons实例中没有其他元素,因此任何大于1的索引都超出范围。

如果要索引列表本身,可以使用apply方法:

print(newMyList(2))