是一种方法还是一类?

时间:2013-10-17 12:17:40

标签: scala

Marting Odersky在他的书中写道:

  

::,发音为“cons”的“构造”,表示非空列表。

  

列表构造方法::和:::是特殊的。因为他们结束了   在冒号中,他们被绑定在右边的操作数上。那是一个   诸如x :: xs之类的操作被视为方法调用xs.::(x),而不是   x.::(xs)。实际上,x。::( xs)没有意义,因为x是列表   元素类型,可以是任意的,所以我们不能假设这一点   type将具有:: 方法。因此,:: 方法应该   获取元素值并生成新列表

::方法或类是什么?

2 个答案:

答案 0 :(得分:16)

这是classmethod。 Cons是一个类型参数化类。 List [A]有两个子类:Cons和Nil。由于Cons是一个类,它可以通过其构造函数创建,如下所示:

val s = new ::[Int](4, Nil)

Cons是一个案例类,我们在进行模式匹配时使用构造函数。 Cons也是列表类中的一个方法,它在两个子类中实现。因此,我们可以在上面创建的cons类中使用cons方法。

val s1 = s.::(5)

可能会出现部分混淆,因为我们通常使用List对象的apply方法创建列表:

val s2 = List(1, 2, 3)

通常,对象的apply方法返回一个与对象同名的Class的新实例。然而,这只是惯例。在这种特殊情况下,List Object返回Cons子类的新实例。 List类本身是一个密封的抽象类,因此无法实例化。因此上述apply方法执行以下操作:

val s2 = 1 :: 2 :: 3 :: Nil

任何以':'结尾的方法都是右侧操作数上的方法,因此可以将其重写为

val s2 = 1 :: (2 :: (3 :: Nil)) //or as
val s2 = Nil.::(3).::(2).::(1)

因此,Nil对象上的Cons(::)方法将3作为参数,并生成Cons类的匿名实例化,其中3为头部,对Nil对象的引用为尾部。让我们称这个匿名对象为c1。然后在c1上调用Cons方法,以2为参数返回一个新的匿名Cons实例,让我们称之为c2,其头部为2,c1为尾部。然后最后c2对象上的cons方法将1作为参数,并返回命名对象s2,其中1为头部,c2为尾部。

第二个困惑点是REPL和Scala工作表使用类'toString方法来显示结果。所以工作表给了我们:

val s3 = List(5, 6, 7)     // s3  : List[Int] = List(5, 6, 7)
val s4 = List()            // s4  : List[Nothing] = List()
val s5: List[Int] = List() // s5  : List[Int] = List()
s3.getClass.getName        // res3: String = scala.collection.immutable.$colon$colon
s4.getClass.getName        // res4: String = scala.collection.immutable.Nil$
s5.getClass.getName        // res5: String = scala.collection.immutable.Nil$

如上所述,List已被密封,因此无法创建新的子子类,因为Nil是一个对象而Cons是final。由于Nil是一个对象,因此无法进行参数化。 Nil继承自List [Nothing]。乍一看听起来不那么有用,但记住这些数据结构是不可变的,所以我们永远不能直接添加它们,Nothing是其他所有类的子类。所以我们可以毫无问题地将Nil类(间接)添加到任何List中。 Cons类有两个成员头和另一个List。当你计时时它是一个相当简洁的解决方案。

我不确定这是否有任何实际用途,但您可以将Cons用作类型:

var list: ::[Int] = List (1,2,3).asInstanceOf[::[Int]]
println("Initialised List")
val list1 = Nil
list = list1.asInstanceOf[::[Int]] //this will throw a java class cast exception at run time
println("Reset List")

答案 1 :(得分:5)

简短回答:两者都有。

列表中有一个名为::的子类,但您不会经常明确地引用它。

当你写作例如1 :: 2 :: Nil :: List :: ::,它在幕后创建了一个类List的实例。

::最好不要考虑作为方法或类,而应该是a method(ADT)意义上的构造函数。维基百科称ADT构造函数为“准功能权利”,这使得它们听起来比它们更复杂,但不一定是思考它们的坏方法。

Nil有两个构造函数::(在某些语言中称为 cons )和def foo(xs: ::[Int]) = ??? 。我上面链接的维基百科文章很好地介绍了列表作为代数数据类型的想法。

值得注意的是,在某些语言(如Haskell)中,ADT构造函数没有与它们关联的自己的类型 - 它们只是创建ADT实例的函数。 这在Scala中通常也是如此,其中很少引用像Some这样的ADT构造函数的类型。但是有可能 - 我们可以这样写:

Option

或者这个(def bar(s: Some[Int]) = ??? 是{{1}} ADT的构造函数之一)。

{{1}}

但这通常不是很有用,可以被认为是Scala实现代数数据类型的工件。