你知道在再见和感谢所有的鱼中的那个场景,亚瑟非常高兴,他停下了服务员并要求知道,“为什么这种食物如此美味?”我处于那种情况。 Scala似乎正在做我想要的,但我不明白它是如何做到的。请考虑以下事项:
scala> var v = Nil:List[String];
v: List[String] = List()
scala> v.length
res38: Int = 0
scala> v ::= "Hello"
scala> v.length
res39: Int = 1
scala> Nil.length
res40: Int = 0
这正是你所希望的,但它是如何发生的?
Nil是扩展List [Nothing]的对象,它是List [String]的子类型,所以赋值工作正常,但它是 immutable 列表,不是吗?所以我不应该追加它。但是我可以附加它,或者至少我可以附加到v,我认为它指向Nil。 v被改变了,但是Nil不是。
那么,WTF? Scala是否有一些我不知道的聪明的复制修改语义? Nil真的是一个返回空列表的函数吗?更广泛地说,有什么方法可以让REPL回答这些问题吗?
答案 0 :(得分:16)
当Scala看到
时v ::= "Hello"
首先检查v
是否知道方法::=
。在这种情况下(类型List
),它不会,因此,因为该方法仅由符号组成,并以=
符号结束,它会尝试其他内容:< / p>
v = v.::("Hello")
顺便提一下,用中缀表示法写成"Hello" :: v
。现在,由于v
知道方法::
,该方法有效,并返回一个新的List
,然后按照指示将其分配给v
。
顺便说一下,它是在前置,而不是附加。
答案 1 :(得分:4)
当您使用关键字var
时,您并非“在Java意义上”转换为final
“。相反,var
允许重新分配。当您致电运营商::=
时,您实际上正在重新分配v
指向的内容。运算符::=
返回 new 列表,而不是附加“Hello”的原始列表。因此,v
现在指向“Hello”而不是Nil
列表。
在这里,请注意:
var myThing = new List(1, 2, 3)
var myOhterThing = myThing
myThing = new List(1, 2, 3, 4)
这与说“获取第一个列表,复制它并在其上附加'4'几乎相同。现在指定'myThing'指向该列表。”使用该运算符,您可以有效地完成相同的操作。用这种方式写出来可以让你看到它。
答案 2 :(得分:3)
尝试使用val v,看看哪些是可变的。 :)
答案 3 :(得分:2)
Nil是不可改变的。当你对它(... :)有所了解时,你会得到一个也是不可变的新实例。
试试这个:
val v1 = Nil
val v2 = "Hello" :: v1
v1 == v2
(因为他们没有指向同一个对象,你会得到假的)
鉴于v是一个var,你可以为它重新分配值。
所以当你有:
v ::= "Hello" // what you really have is:
v = "Hello" :: v // or
v = "Hello" :: Nil // or
v = List("Hello")
以上创建了一个新List,Nil保持不变。它类似于Java中的String添加。由于String是不可变的,因此您永远不会更改单个实例 - 只创建新实例。
由于Nil没有改变,Nil.length = 0。