Scala - 中缀与点符号

Posted

技术标签:

【中文标题】Scala - 中缀与点符号【英文标题】:Scala - infix vs dot notation 【发布时间】:2012-05-01 06:31:06 【问题描述】:

是否有一种最佳做法?我一直在阅读 Odersky 等人的 Scala 书。似乎中缀用于很多 Collections API 函数,而 dot 是为程序员定义的函数保留的。

【问题讨论】:

【参考方案1】:

我个人对此没有任何硬性规定,但我倾向于仅对符号方法名称使用中缀表示法,对字母数字名称使用点表示法。

中缀表示法使以后修改代码很麻烦。以下是一些示例。

假设你有这行代码:

xs filter  f  map  g 

假设在稍后的某个时间点,您需要在末尾添加一个toList。你这样说:

xs filter  f  map  g  toList

这可能会导致分号推断问题。为避免这些问题,您可以在末尾添加分号,或者换行。在我看来,这两种选择都很丑陋。为了避免所有这些废话,我更喜欢使用xs.filter(f).map(g)。使用这种语法重构总是更容易。

另一个例子:假设我的代码中有以下内容:

if(foo contains bar)  ..

说,我需要否定条件。如果我修改如下:

if(!foo contains bar)  ..

无赖。这被解析为(!foo).contains(bar)。不是我们想要的。

或者假设你需要另外添加一个新的条件,你修改成这样:

if(foo contains bar && cond)  ..

又是一个无赖。这被解析为foo.contains(bar.&&(cond))。又不是我们想要的。

当然,您可以在周围添加一堆括号,但与点符号相比,这会很丑陋且难以阅读/编辑。

现在,我上面所说的所有内容也适用于符号方法名称。但是符号方法在与点语法一起使用时看起来不自然,因此我更喜欢它们的中缀语法。


上述指南的一个例外:内部 DSL。它们通常精心制作,以免在以文档/示例中规定的方式(通常使用中缀符号)编写时导致解析问题。

【讨论】:

同样的逻辑也可以用于支持中缀表示法,因为它提供了一种额外的机制来控制优先级(除了选择运算符)。并且它使优先级在视觉上更加明显。例如,尝试用真正的函数重写你的第一个示例,而不仅仅是提升方法:(xs filter f map g).toList 仍然比xs.filter(f).map(g).toList) 更清晰、更明显 加上括号需要非线性编辑,这是避免中缀符号的另一个原因。放下大括号也无济于事。 去掉括号就是重点。当结合运算符的相对“强度”和括号/大括号的使用时,能够使用中缀与点符号来控制优先级是保持代码整洁的非常强大的工具。 而且,是的,不加点的 POSTfix 方法是一件坏事(tm)。但是用它们来批评 INfix 方法调用有点像稻草人的谬误。 someExpression => someExpression.newPart 并不明显优于someExpression => (someExpression).newPart 在这两种情况下,您都必须添加.newPart。诚然,您必须在第二种情况下添加括号,但您一开始可能会少很多。稻草人是用“这可能会导致分号推断问题”来证明前一种方法的合理性。解释 - 这是添加后缀调用的症状,而不是原始表达式。【参考方案2】:

这是个人喜好问题。您决定使用一种风格还是另一种风格应该基于使您的代码最易读的因素。

但请注意,省略点和括号的功能仅限于某些句法结构,因此有时您只需要退回使用它们即可。

【讨论】:

还要注意a.op(b)a op b 长,但比(a op b) 短。细微差别,但它可以成为“最易读”考虑的一部分。 您可以使用 a?b 代替 a.?(b) 或 a ? b 如果您需要保存每个字节。 :) 当然,像这样命名的方法的数量是有限的。【参考方案3】:

Scala 官方站点文档中有一个很好的style guide,它描述了中缀表示法优于点表示法的正确用法。

后缀符号:

names.toList
// is the same as
names toList // Unsafe, don't use!

Arity-1:

// right!
names foreach (n => println(n))
names mkString ","
optStr getOrElse "<empty>"
// wrong!
javaList add item

高阶函数:

// wrong!
names.map (_.toUpperCase).filter (_.length > 5)
// right!
names map (_.toUpperCase) filter (_.length > 5)

符号方法/运算符:

// right!
"daniel" + " " + "Spiewak"
// wrong!
"daniel"+" "+"spiewak"

【讨论】:

【参考方案4】:

我发现当我使用 cats 库创建笛卡尔坐标时,使用 map 的中缀表示法效果很好。例如:

(fetchIt(1) |@| fetchIt(2) |@| fetchIt(3)).map(MyCaseClass)

你可以像这样去掉周围的括号:

fetchIt(1) |@| fetchIt(2) |@| fetchIf(3) map MyCaseClass

在我看来,第二个变体读起来更好。我想是口味问题。只是想增加我两美分的价值。

上述方法有效,因为“|@|”中的|在“地图”中具有比m 更高的优先级。阅读 Scala 语言规范的这一部分以了解更多详细信息:

如果一个表达式中有多个中缀操作,那么操作符 具有较高优先级的运算符比具有较低优先级的运算符绑定得更紧密 优先级。

http://scala-lang.org/files/archive/spec/2.12/06-expressions.html#infix-operations

【讨论】:

以上是关于Scala - 中缀与点符号的主要内容,如果未能解决你的问题,请参考以下文章

反向波兰符号的中缀

28.scala的运算符

C++中中缀符号的前缀

中缀表达式转换成为后缀表达式

栈的应用---中缀变后缀

学习惯用 Haskell 的资源(eta 缩减、符号中缀运算符、库等)[关闭]