简短的回答是,右关联可以通过使程序员键入的内容与程序实际执行的内容一致来提高可读性。
所以,如果你输入 ' 1 :: 2 :: 3
',你会得到一个 List(1, 2, 3),而不是以完全不同的顺序得到一个 List。
那是因为 ' 1 :: 2 :: 3 :: Nil
' 实际上是
List[Int].3.prepend(2).prepend(1)
scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)
两者都是:
- 更具可读性
- 更有效(O(1) 用于
prepend
,而 O(n) 用于假设append
方法)
(提醒,摘自《Scala 编程》一书)
如果以运算符表示法使用方法,例如a * b
,则在左操作数上调用该方法,如a.*(b)
— 除非方法名称以冒号结尾。
如果方法名称以冒号结尾,则在右操作数上调用该方法。
因此,在 中1 :: twoThree
,::
方法在 上调用twoThree
,传入 1,如下所示:twoThree.::(1)
。
对于List,它起到了追加操作的作用(列表似乎是在'1'之后追加形成' 1 2 3
',实际上它是1,它被追加到列表中)。
类 List 不提供真正的追加操作,因为追加到列表所需的时间随列表的大小线性增长,而在前面添加 :: 需要常量时间。
myList :: 1
会尝试将 myList 的全部内容添加到“1”,这将比在 myList 中添加 1 更长(如“ 1 :: myList
”)
注意:不管运算符有什么关联性,它的操作数总是从左到右计算。
因此,如果 b 是一个不仅仅是对不可变值的简单引用的表达式,那么 a ::: b 更精确地被视为以下块:
{ val x = a; b.:::(x) }
在这个块中,a 仍然在 b 之前被评估,然后这个评估的结果作为操作数传递给 b 的 ::: 方法。
为什么要区分左关联方法和右关联方法?
这允许1 :: myList
在实际将操作应用于右表达式的同时保持通常的左关联操作 (' ') 的外观,因为;
- 它更有效。
- 但使用逆关联顺序('
1 :: myList
' vs. ' myList.prepend(1)
')更具可读性
据我所知,正如你所说,“语法糖”。
请注意,例如,在 的情况foldLeft
下,它们可能走得有点远(与 ' /:
' 右结合运算符等效)
为了包含您的一些评论,稍微改写一下:
如果你考虑一个“附加”函数,左关联,那么你会写“ oneTwo append 3 append 4 append 5
”。
但是,如果要将 3、4 和 5 附加到 oneTwo(您会按照它的编写方式假设),它将是 O(N)。
如果是“追加”,则与 '::' 相同。但事实并非如此。它实际上是“前置”
这意味着 ' a :: b :: Nil
' 是 ' List[].b.prepend(a)
'
如果 '::' 在前面添加但仍保持左关联,则结果列表的顺序将错误。
您可能希望它返回 List(1, 2, 3, 4, 5),但它最终会返回 List(5, 4, 3, 1, 2),这可能出乎程序员的意料。
那是因为,您所做的将是按照左关联顺序:
(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)
因此,右关联性使代码与返回值的实际顺序相匹配。