简而言之,右关联性可以通过使程序员键入的内容与程序实际执行的内容一致来提高可读性。
所以,如果你输入 '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
method)
(温馨提示,摘自书中Scala 编程)
如果一个方法用在运算符表示法中,例如a * b
,该方法在左侧操作数上调用,如下所示a.*(b)
— 除非方法名称以冒号结尾。
如果方法名称以冒号结尾,则在右侧操作数上调用该方法。
因此,在1 :: twoThree
, the ::
方法被调用于twoThree
,传入 1,如下所示:twoThree.::(1)
.
对于List来说,它起到了追加操作的作用(列表好像是追加在‘1’后面,形成‘1 2 3
',实际上它是 1前置的到列表)。
类 List 不提供真正的追加操作,因为追加到列表所需的时间随着列表的大小线性增长,而前面加上 :: 需要恒定的时间.
myList :: 1
会尝试将 myList 的整个内容添加到“1”,这比将 1 添加到 myList 更长(如“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)
因此,右关联性使代码与返回值的实际顺序相匹配。