Scala中的右关联方法有什么用?

Mik*_*oss 31 scala operators operator-precedence

我刚刚开始使用Scala,我刚学会了如何使方法成为正确关联(而不是在命令式面向对象语言常见的更传统的左关联性).

首先,当我cons在Scala中看到列表的示例代码时,我注意到每个示例总是在右侧有List:

println(1 :: List(2, 3, 4))
newList = 42 :: originalList
Run Code Online (Sandbox Code Playgroud)

然而,即使在一遍又一遍地看到这一点之后,我也没有三思而后行,因为我不知道(当时)这::是一种方法List.我只是假设它是一个运算符(再次,在Java中的运算符意义上),并且关联性并不重要.List在示例代码中始终出现在右侧的事实似乎巧合(我认为它可能只是"首选样式").

现在我知道的更好:它必须以这种方式编写,因为它::是正确联想的.

我的问题是,能够定义右关联方法的重点是什么?

这纯粹是出于审美原因,还是在某些情况下,权利关联实际上可以带来某些与左联结相关的好处?

从我(新手)的角度来看,我真的不知道如何

1 :: myList
Run Code Online (Sandbox Code Playgroud)

比任何更好

myList :: 1
Run Code Online (Sandbox Code Playgroud)

但这显然是一个微不足道的例子,我怀疑这是一个公平的比较.

Von*_*onC 37

简短的回答是,右关联性可以通过使程序员类型与程序实际执行的内容一致来提高可读性.
因此,如果键入' 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)
Run Code Online (Sandbox Code Playgroud)

这两者都是:

  • 更具可读性
  • 效率更高(O(1)for prepend,O(n)for hypothetic appendmethod)

(提醒,摘自Scala中的Programming)
如果在运算符表示法中使用了一个方法,例如a * b,在左操作数上调用该方法,如同a.*(b)- 除非方法名以冒号结尾.
如果方法名称以冒号结尾,则在右操作数上调用该方法.
因此,在1 :: twoThree,::调用方法twoThree,传入1,如下所示:twoThree.::(1).

对于List,它扮演一个追加操作的角色(列表似乎附加在'1'之后形成' 1 2 3',实际上它是1,它被添加到列表前面).
类列表不提供真正的追加操作,因为附加到列表所花费的时间随着列表的大小线性增长,而前置为::需要恒定时间.
myList :: 1会尝试将myList的整个内容添加到'1',这比将1添加到myList(在' 1 :: myList'中)更长

注意:无论操作员具有什么关联性,其操作数始终从左到右进行评估.
因此,如果b是一个不仅仅是对不可变值的简单引用的表达式,那么::: b更精确地被视为以下块:

{ val x = a; b.:::(x) }
Run Code Online (Sandbox Code Playgroud)

在这个块中,仍然在b之前评估a,然后将该评估的结果作为操作数传递给b的:::方法.


为什么要区分左联合方法和右联结方法呢?

这允许1 :: myList在实际对正确表达式应用操作时保持通常的左关联操作(' ')的外观,因为;

  • 它效率更高.
  • 但它具有反向关联顺序(' 1 :: myList'vs.' myList.prepend(1)')更具可读性

正如你所说,据我所知,"语法糖".
请注意,foldLeft例如,在他们可能已经走得有点远的情况下(使用' /:'右关联运算符等效)


要包含一些评论,请稍微改写一下:

如果你考虑一个'append'函数,left-associative,那么你会写' 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)
Run Code Online (Sandbox Code Playgroud)

因此,右关联性使代码与返回值的实际顺序匹配.