List 为什么在scala中创建列表时需要Nil?

List 为什么在scala中创建列表时需要Nil?,list,scala,List,Scala,清单上有一个基本问题 当我尝试使用cons运算符创建列表时,出现以下错误 scala> val someList = 1::2 <console>:10: error: value :: is not a member of Int val someList = 1::2 ^ 我想知道为什么在我们创建一个列表时,至少需要一次Nil Nil是数据类型吗?还是空元素?正是因为这个原因 value::不是Int的成员 在Scala中,运算

清单上有一个基本问题

当我尝试使用cons运算符创建列表时,出现以下错误

scala> val someList = 1::2
<console>:10: error: value :: is not a member of Int
   val someList = 1::2
                   ^
我想知道为什么在我们创建一个列表时,至少需要一次Nil


Nil是数据类型吗?还是空元素?

正是因为这个原因

value::不是Int的成员

在Scala中,运算符实际上是对象上的函数。在本例中,
Nil
对象上的函数,该对象实际上是一个空列表对象

当您执行
1::2
时,Scala会在
2
上查找名为
的函数,但找不到该函数。这就是它失败的原因



注意:在Scala中,如果运算符的最后一个字符不是冒号,则在第一个操作数上调用运算符。例如,
1+2
基本上是
1.+(2)
。但是,如果最后一个字符是冒号,则在右侧操作数上调用运算符。所以在这种情况下,
1::Nil
实际上是
Nil::(1)
。由于
::
返回另一个列表对象,您可以将其链接,就像
1::2::Nil
实际上是,
Nil:::(2)。:(1)
名称以
结尾的中缀运算符被解释为右操作数上的方法调用。因此
1::2
2:::(1)
,即它调用
2
上的方法
。类似地,
1::2::Nil
Nil.:::(2)。:(1)


第一个方法不起作用的原因是
2
Int
,而
Int
s没有
方法。第二种方法之所以有效,是因为
Nil
是一个列表,而列表确实有一个
方法。由于
列表的结果。:
也是一个列表,您仍然可以对第一个
的结果调用

Scala中以冒号结尾的任何运算符都定义在右操作数上。 因此,当您编写
1::2
时,意味着
应该在
2
上定义,即在
Int
上定义,但事实并非如此


Nil
List
的一个值,上面定义了一个方法
。因此,当您编写
1::2::Nil
时,它被评估为
(Nil::(1)

Nil
是创建
列表作为递归数据结构的基本构建块。列表是一种有用的数据结构,它提供对头部(第一个元素)的恒定时间访问

列表在它的最小核心,是建立在3个操作之上的
,和
Nil
List
的单例子类,因此它是同类实例中的一个特殊实例,表示空列表。cons操作符
在列表上定义,通过在列表中的一个元素前面加前缀,递归地构建一个列表

参见trait
列表
和object
Nil
的定义(高度简化)

由于任何以
结尾的标识符/运算符都与右侧关联,
运算符也与右侧关联

编写
1::2::3
时,scala会尝试将此调用重写为
3::(2::(1))
。i、 E3成为第一次调用
的接收者,该调用在任何任意数据类型(本例中为Int)上都不存在

这就是为什么你总是在一个空的列表上建立一个空的列表-Nil。可以将其视为在空列表
Nil
上一个接一个地为每个元素添加前缀

    scala> val someList = 1::2::Nil
    someList: List[Int] = List(1, 2)
scala> Nil
res0: scala.collection.immutable.Nil.type = List()
trait List[A] {
    def head: A
    def tail: List[A]
    def isEmpty: Boolean
}
case object Nil extends List[Nothing] {
    def head = throw new NoSuchElementException("head of empty list")
    def tail = throw new UnsupportedOperationException("tail of empty list")
    def isEmpty = true
}