Подтвердить что ты не робот

Когда использовать скобки в нотации 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) я могу использовать функцию отображения.

Почему этот конкретный случай нуждается в круглых скобках? Есть ли общая рекомендация о том, когда и зачем вам это нужно?

4b9b3361

Ответ 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: Операторы.