Когда использовать скобки в нотации infix Scala
При программировании в 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 (файл) 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) я могу использовать функцию отображения.
Почему этот конкретный случай нуждается в круглых скобках? Есть ли общая рекомендация о том, когда и зачем вам это нужно?
Ответы
Ответ 1
Вот что я собрал для себя после прочтения спецификации:
- Любой метод, который принимает один параметр, может использоваться в качестве инфиксного оператора:
a.m(b)
можно записать a m b
.
- Любой метод, который не требует параметра, может использоваться как постфиксный оператор:
a.m
можно записать a m
.
Например, a.##(b)
может быть записано a ## b
и a.!
может быть записано a!
- Операторы Postfix имеют более низкий приоритет, чем операторы infix, поэтому
foo bar baz
означает foo.bar(baz)
, а foo bar baz bam
означает (foo.bar(baz)).bam
и foo bar baz bam bim
означает (foo.bar(baz)).bam(bim)
.
- Также задан безпараметрический метод m объекта a,
a.m.m
, но a m m
не так, как он будет анализировать как 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
не имеет смысла как list.mkString(map).function
, вот как он будет разбираться.
Ответ 2
Scala ссылка упоминает (6.12.3: Prefix, In fi x и Post fi x Operations)
В последовательности последовательного типа в операциях fi x t0 op1 t1 op2 . . .opn tn
все операторы op1, . . . , opn
должны иметь одинаковую ассоциативность. Если они все лево-ассоциативные, последовательность интерпретируется как (. . . (t0 op1 t1) op2 . . .) opn tn
.
В вашем случае "map
" не является термином для оператора "mkstring
", поэтому вам нужна группировка (со скобкой вокруг "fromFile(file) mkString
" )
Собственно, Matt R комментирует:
Это не проблема ассоциативности, более того, что " операторы Postfix всегда имеют более низкий приоритет, чем в операциях fi x. Например, e1 op1 e2 op2
всегда эквивалентно (e1 op1 e2) op2
". (Также из 6.12.3)
huynhjl answer (upvoted) дает более подробную информацию и Марк Буш answer (также отмечен) указывает на " Прогулка по Scala: Операторы", чтобы проиллюстрировать, что "Любой метод, который принимает один параметр, может использоваться как оператор инфикса".
Ответ 3
Здесь простое правило: никогда не использовались постфиксные операторы. Если вы это сделаете, поставьте полное выражение, заканчивающееся оператором postfix внутри скобок.
Фактически, начиная с Scala 2.10.0, выполнение этого будет генерировать предупреждение по умолчанию.
Для хорошей меры вам может потребоваться переместить оператор postfix и использовать для него точечную нотацию. Например:
(fromFile(file)).mkString map caesar(k)_
Или, что еще проще,
fromFile(file).mkString map caesar(k)_
С другой стороны, обратите внимание на методы, в которых вы можете предоставить пустую скобку, чтобы превратить их в infix:
fromFile(file) mkString () map caesar(k)_
Ответ 4
Спецификация не дает ясности, но мой опыт и эксперименты показали, что компилятор Scala всегда будет пытаться обрабатывать вызовы методов с использованием нотации infix в качестве инфиксных операторов. Несмотря на то что ваше использование mkString является постфиксным, компилятор пытается интерпретировать его как инфикс и поэтому пытается интерпретировать "карту" в качестве аргумента. Все применения постфиксных операторов должны либо немедленно сопровождаться терминатором выражения, либо использоваться с "точечной" записью для компилятора, чтобы увидеть его как таковой.
Вы можете получить намек на это (хотя это не указано) в Прогулка по Scala: Операторы.