0

今天是我学习 Scala 的第一天,我学到的东西之一是初始化列表的常用方法是结合 Nil(空列表)和 ::(前置方法)。

假设我以这种方式初始化列表:

val myList = List("A", "B", "C")

这与以下内容相同:

val myList = "A" :: "B" :: "C" :: Nil

我知道您可以阅读第二个块,如下所示:

  1. 从一个空列表开始
  2. 将“C”添加到该列表的开头。该列表不再为空。
  3. 将“B”添加到列表的开头。
  4. 将“A”添加到列表的开头。
  5. 将列表分配给不可变的 List 集合。推断每个列表元素的数据类型(字符串)。

我不明白的是为什么 Nil 的行为与 NULL 不一样?当以这种方式初始化时,为什么列表末尾没有 NULL 值?

(原谅我,OOP 和 FP 不是我的菜,但我想学习。)

4

2 回答 2

2

如果您希望能够调用所有列表上的方法,包括空列表(例如Nil.length),Nil则必须是对象,而不是null。否则,您将拥有NullPointerException而不是0.

这也是类型安全所必需的。Nil是 type 的值List[Nothing],即List[A]for all的值A(因为List是协变的)。并且null是类型的值,Null即所有引用类型的值(因为Null它是任何引用类型的子类型),而不仅仅是列表类型。

于 2020-05-18T13:54:17.107 回答
1

要学的东西太多了。你首先需要了解什么是对象,什么是类,什么是接口/特征,什么是值,什么是类型。然后有了这些,您可能需要了解ADT

我会尽量快速回答这个问题。

List数据类型定义为

sealed trait List[+A]
final case class ::[+A](head: A, tail: List[A]) extends List[A]
final case object Nil extends List[Nothing]

所以在这里我们可以看到三样东西:一个trait,一个class和一个object

  • 类是用于创建对象(也称为实例)内存蓝图。它不是一个值,要使用它,您必须调用它的构造函数(在这种情况下,它接收两个值, the和 the 来获取该类的新值。headtail
  • 对象就像一个已经实例化的匿名类,因此它已经是一个值,您可以按原样使用它。
  • 一个trait就像一个不能被实例化的类,它被称为是抽象的。它用于定义所有子类必须遵循的常见行为。

此外,当特征是密封的并且类和对象是cases时,我们称这些ADT (代数数据类型)
不要让花哨的名字吓到你,它只是意味着特征代表一个单一的类型(在这种情况下是 List)并且所有的情况都代表这种类型的部分。
ADT 由乘积和总和组成,总和代表备选方案(它是 A 或 B),乘积代表连词(它是 A 和 B)
List的情况下,我们说它要么是空列表( Nil) ,要么是具有自己的头部和尾部的 cons ( ::) ,其中尾部是另一个列表。

有了这一切,并且知道 Scala 有一个语法技巧来允许在中间使用符号名称,您可以看到:

val list = 1 :: 2 :: 3 :: Nil

是相同的:

val list = new ::(
  head = 1,
  tail = new ::(
    head = 2,
    tail = new ::(
      head = 3,
      tail = Nil
    )
  )
)

奖金:

  • 类型多于类
  • null它是Null类型 的值(习惯这种类型的唯一值),它是所有AnyRef类型的子类型。然而,它是一种特殊的价值。因为如果您尝试以任何方式对其进行操作,它将失败。我的建议,忘记它的存在,这是一个错误,它只存在于Java互操作中。如果您需要建模没有值,请使用Option.
于 2020-05-18T14:11:55.897 回答