何时在Scala中缀表示法中使用括号

时间:2011-04-08 08:45:22

标签: scala dictionary functional-programming parentheses infix-notation

在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 个答案:

答案 0 :(得分:35)

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

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

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

  • 后缀运算符的优先级低于中缀运算符,因此foo bar baz表示foo.bar(baz),而foo bar baz bam表示(foo.bar(baz)).bamfoo bar baz bam bim表示(foo.bar(baz)).bam(bim)
  • 同时给定对象 m 的无参数方法 m a.m.m有效但a m m不会像{{1}那样解析}}。

由于exp1 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,但这就是它的解析方式。

答案 1 :(得分:5)

Scala reference提及(6.12.3:Pre fi x,In fi x和Post fi x Operations)

  

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

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


实际上,Matt R评论:

  

这不是一个真正的关联性问题,更多的是“ Post fi x运算符的优先级始终低于运算符。例如,e1 op1 e2 op2总是等于(e1 op1 e2) op2”。 (也来自6.12.3)

huynhjlanswer(upvoted)提供了更多详细信息,而Mark Bushanswer(也是upvoted)指向“A Tour of Scala: Operators”来说明“任何采用单个参数的方法都可以用作中缀运算符”。

答案 2 :(得分:4)

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

实际上,从Scala 2.10.0开始,这样做会默认生成警告。

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

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

或者,更简单地说,

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

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

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

答案 3 :(得分:2)

规范并不清楚,但我的经验和实验表明,Scala编译器总是尝试使用中缀表示法作为中缀运算符来处理方法调用。即使您使用mkString是后缀,编译器也会尝试将其解释为中缀,因此尝试将“map”解释为其参数。 postfix运算符的所有用法必须紧跟一个表达式终止符,或者与“dot”表示法一起使用,编译器才能看到它。

你可以在A Tour of Scala: Operators中得到一些暗示(虽然没有拼写出来)。