Scala中的右关联方法有什么用? [英] What good are right-associative methods in Scala?

查看:81
本文介绍了Scala中的右关联方法有什么用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我刚刚开始玩Scala,我刚刚学习了如何使方法成为 right-associative (与更传统的 left-associativity 相对)在命令式面向对象语言中很常见).

I've just started playing around with Scala, and I just learned about how methods can be made right-associative (as opposed to the more traditional left-associativity common in imperative object-oriented languages).

首先,当我在Scala中看到示例代码cons的列表时,我注意到每个示例始终在右侧都有该列表:

At first, when I saw example code to cons a list in Scala, I had noticed that every example always had the List on the right-hand side:

println(1 :: List(2, 3, 4))
newList = 42 :: originalList

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

However, even after seeing this over and over again, I didn't think twice about it, because I didn't know (at the time) that :: is a method on List. I just assumed it was an operator (again, in the sense of operator in Java) and that associativity didn't matter. The fact that the List always appeared on the right-hand side in example code just seemed coincidental (I thought it was maybe just the "preferred style").

现在我知道的更好:它必须这样写,因为::是右关联的.

Now I know better: it has to be written that way because :: is right-associative.

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

这纯粹是出于审美原因,还是在某些情况下右联想会比左联想有某种好处?

Is it purely for aesthetic reasons, or can right-associativity actually have some kind of benefit over left-associativity in certain situations?

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

From my (novice) point-of-view, I don't really see how

1 :: myList

myList :: 1

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

but that's obviously such a trivial example that I doubt it's a fair comparison.

推荐答案

简而言之,就是通过使程序员类型与程序实际行为保持一致,右联想可以提高可读性.
因此,如果键入"1 :: 2 :: 3",则会返回一个List(1、2、3),而不是以完全不同的顺序返回一个List.
那是因为'1 :: 2 :: 3 :: Nil'实际上是

The short answer is that right-associativity can improve readability by making what the programmer type consistent with what the program actually does.
So, if you type '1 :: 2 :: 3', you get a List(1, 2, 3) back, instead of getting a List back in a completely different order.
That would be because '1 :: 2 :: 3 :: Nil' is actually

List[Int].3.prepend(2).prepend(1)

scala> 1 :: 2 :: 3:: Nil
res0: List[Int] = List(1, 2, 3)

两者都是:

  • 更具可读性
  • 更高效(prepend的O(1),假设append方法的O(n))
  • more readable
  • more efficient (O(1) for prepend, vs. O(n) for an hypothetic append method)

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

(Reminder, extract from the book Programming in Scala)
If a method is used in operator notation, such as a * b, the method is invoked on the left operand, as in a.*(b) — unless the method name ends in a colon.
If the method name ends in a colon, the method is invoked on the right operand.
Therefore, in 1 :: twoThree, the :: method is invoked on twoThree, passing in 1, like this: twoThree.::(1).

对于列表,它起到附加操作的作用(列表似乎被附加在'1'之后以形成'1 2 3',实际上它是前缀的1).到列表).
类列表"没有提供真正的附加操作,因为附加到列表所需的时间随列表的大小线性增长,而以::开头需要花费固定的时间.
myList :: 1会尝试将myList的全部内容添加到'1'之前,这比将1填充到myList之前要长(如'1 :: myList')

For List, it plays the role of an append operation (the list seems to be appended after the '1' to form '1 2 3', where in fact it is 1 which is prepended to the list).
Class List does not offer a true append operation, because the time it takes to append to a list grows linearly with the size of the list, whereas prepending with :: takes constant time.
myList :: 1 would try to prepend the entire content of myList to '1', which would be longer than prepending 1 to myList (as in '1 :: myList')

注意:无论运算符具有什么关联性,其操作数都是 始终从左到右求值.
因此,如果b是一个表达式,而不仅仅是对不可变值的简单引用,则::: b更精确地视为以下块:

Note: No matter what associativity an operator has, however, its operands are always evaluated left to right.
So if b is an expression that is not just a simple reference to an immutable value, then a ::: b is more precisely treated as the following block:

{ val x = a; b.:::(x) }

在此块中,a仍在b之前求值,然后求值结果 作为操作数传递给b的:::方法.

In this block a is still evaluated before b, and then the result of this evaluation is passed as an operand to b’s ::: method.

为什么要完全区分左联想法和右联想法?

why make the distinction between left-associative and right-associative methods at all?

这可以保留通常的左联想运算('1 :: myList')的外观,同时在右表达式上实际应用该运算,因为;

That allows to keep the appearance of a usual left-associative operation ('1 :: myList') while actually applying the operation on the right expression because;

  • 效率更高.
  • 但以相反的关联顺序('1 :: myList'与'myList.prepend(1)')比较可读
  • it is more efficient.
  • but it is more readable with an inverse associative order ('1 :: myList' vs. 'myList.prepend(1)')

据我所知,您说的是句法糖".
请注意,例如,对于foldLeft,它们可能具有一点点走了(与等效的'/:'右关联运算符)

So as you say, "syntactic sugar", as far as I know.
Note, in the case of foldLeft, for instance, they might have gone a little to far (with the '/:' right-associative operator equivalent)

要包含您的一些评论,请稍作措辞:

To include some of your comments, slightly rephrased:

如果您考虑使用添加"功能(左关联),则应编写"oneTwo append 3 append 4 append 5".
但是,如果将3、4和5附加到oneTwo(您将以其编写的方式假定),则它将为O(N).
与"::"相同,如果用于追加".但事实并非如此.它实际上是为"prepend"

if you consider an 'append' function, left-associative, then you would write 'oneTwo append 3 append 4 append 5'.
However, if it were to append 3, 4, and 5 to oneTwo (which you would assume by the way it's written), it would be O(N).
Same with '::' if it was for "append". But it is not. It is actually for "prepend"

这意味着'a :: b :: Nil'代表'List[].b.prepend(a)'

如果'::'在前面,但仍保持左联想,则结果列表将以错误的顺序排列.
您可能希望它返回List(1、2、3、4、5),但最终会返回List(5、4、3、1、2),这对于程序员来说可能是意外的.
这是因为,您要做的就是按照左关联顺序进行的操作:

If '::' were to prepend and yet remain left-associative, then the resulting list would be in the wrong order.
You would expect it to return List(1, 2, 3, 4, 5), but it would end up returning List(5, 4, 3, 1, 2), which might be unexpected to the programmer.
That is because, what you have done would have been, in the left-associative order:

(1,2).prepend(3).prepend(4).prepend(5) : (5,4,3,1,2)

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

So, the right-associativity makes the code match up with the actual order of the return value.

这篇关于Scala中的右关联方法有什么用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆