15

在 Scala 中编程时,我会做越来越多的函数式工作。但是,在使用中缀表示法时,很难区分何时需要括号,何时不需要。

例如下面的一段代码:

def caesar(k:Int)(c:Char) = c match {
    case c if c isLower => ('a'+((c-'a'+k)%26)).toChar
    case c if c isUpper => ('A'+((c-'A'+k)%26)).toChar
    case _ => c
}

def encrypt(file:String,k:Int) = (fromFile(file) mkString) map caesar(k)_

(fromFile(file) mkString) 需要括号才能编译。删除后,我收到以下错误:

Caesar.scala:24: error: not found: value map
    def encrypt(file:String,k:Int) = fromFile(file) mkString map caesar(k)_
                                                                 ^
one error found

mkString 显然返回一个字符串(通过隐式转换 AFAIK)我可以使用 map 函数。

为什么这种特殊情况需要括号?是否有关于何时以及为什么需要它的一般指导方针?

4

4 回答 4

35

这是我在阅读规范后为自己整理的:

  • 任何采用单个参数的方法都可以用作中缀运算符:a.m(b)可以编写a m b
  • 任何不需要参数的方法都可以用作后缀运算符:a.m可以写a m

比如a.##(b)可以写a ## ba.!可以写a!

  • 后缀运算符的优先级低于中缀运算符,因此foo bar bazmean和mean 。foo.bar(baz)foo bar baz bam(foo.bar(baz)).bamfoo bar baz bam bim(foo.bar(baz)).bam(bim)
  • 还给定对象a的无参数方法m,它是有效的,但不像它解析为。a.m.ma m mexp1 op exp2

因为有一个版本mkString采用单个参数,所以它将被视为fromFile(file) mkString map caesar(k)_. 还有一个mkString不带参数的版本可以用作后缀运算符:

scala> List(1,2) mkString
res1: String = 12

scala> List(1,2) mkString "a"
res2: String = 1a2

有时通过在正确的位置添加点,您可以获得所需的优先级,例如fromFile(file).mkString map { }

所有这些优先级的事情都发生在打字和其他阶段之前,所以即使list mkString map function没有意义list.mkString(map).function,这也是它的解析方式。

于 2011-04-08T15:17:01.247 回答
5

Scala 参考文献提到(6.12.3:前缀、中缀和后缀操作)

在一系列连续的类型中缀操作t0 op1 t1 op2 . . .opn tn中,所有运算符op1, . . . , opn必须具有相同的关联性。
如果它们都是左结合的,则序列被解释为(. . . (t0 op1 t1) op2 . . .) opn tn

在您的情况下,“ map”不是运算符“ mkstring”的术语,因此您需要分组(括号围绕“ fromFile(file) mkString”)


实际上,马特 R评论说:

这并不是真正的关联性问题,更多的是“后缀运算符的优先级总是低于中缀运算符。例如e1 op1 e2 op2,总是等价于(e1 op1 e2) op2”。(同样来自 6.12.3)

huynhjl回答(赞成)提供了更多细节,而Mark Bush回答(也赞成)指向“ A Tour of Scala: Operators ”以说明“任何采用单个参数的方法都可以用作中缀运算符” .

于 2011-04-08T10:39:18.327 回答
4

这是一个简单的规则:从不使用后缀运算符。如果这样做,请将以后缀运算符结尾的整个表达式放在括号内。

事实上,从 Scala 2.10.0 开始,默认情况下这样做会产生警告。

为了更好地衡量,您可能希望将后缀运算符移出,并使用点表示法。例如:

(fromFile(file)).mkString map caesar(k)_

或者,更简单地说,

fromFile(file).mkString map caesar(k)_

另一方面,请注意可以提供空括号将它们转换为中缀的方法:

fromFile(file) mkString () map caesar(k)_
于 2011-04-08T19:01:10.200 回答
2

规范没有说清楚,但我的经验和实验表明,Scala 编译器总是会尝试将使用中缀表示法的方法调用视为中缀运算符。即使您对 mkString 的使用是后缀,编译器也会尝试将其解释为中缀,因此会尝试将“map”解释为其参数。后缀运算符的所有用法都必须紧跟表达式终止符,或者与“点”表示法一起使用,以便编译器能够看到它。

您可以在A Tour of Scala: Operators中得到一些提示(尽管没有详细说明)。

于 2011-04-08T13:44:15.647 回答