Когда использовать скобки в нотации Scala Infix - PullRequest
15 голосов
/ 08 апреля 2011

При программировании на 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)_

The (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) я могу использовать функцию карты.

Почему в этом конкретном случае нужны скобки?Существует ли общее руководство по поводу того, когда и зачем оно вам нужно?

Ответы [ 4 ]

35 голосов
/ 08 апреля 2011

Это то, что я собрал для себя после прочтения спецификации:

  • Любой метод, который принимает один параметр, может быть использован в качестве инфиксного оператора: a.m(b) можно записать a m b.
  • Любой метод, который не требует параметра, может использоваться в качестве постфиксного оператора: a.m можно записать a m.

Например, можно написать a.##(b) a ## b и a.! могут быть записаны a!

  • Постфиксные операторы имеют более низкий приоритет, чем инфиксные операторы , поэтому 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, именно так оно и будет анализироваться.

5 голосов
/ 08 апреля 2011

Ссылка Scala упоминает (6.12.3: Операции с префиксом, инфиксом и после)

В последовательности последовательных операций типа t0 op1 t1 op2 . . .opn tn все операторы op1, . . . , opn должны иметь одинаковую ассоциативность.
Если все они левоассоциативны, последовательность интерпретируется как (. . . (t0 op1 t1) op2 . . .) opn tn.

В вашем случае 'map' не является термином для оператора 'mkstring', поэтому вам нужно сгруппировать (с круглыми скобками вокруг 'fromFile(file) mkString')


На самом деле, Мэтт R комментарии:

Это на самом деле не проблема ассоциативности, более того, что " Операторы Postfix всегда имеют более низкий приоритет, чем операторы . Например, e1 op1 e2 op2 всегда эквивалентно (e1 op1 e2) op2". (Также с 6.12.3)

huynhjl answer (upvoted) дает более подробную информацию, а Mark Bush s answer (также upvoted) указывают на « Путешествие по Scala: Операторы », чтобы проиллюстрировать, что «Любой метод, который принимает один параметр, может использоваться как инфиксный оператор».

4 голосов
/ 08 апреля 2011

Вот простое правило: никогда не используются постфиксные операторы. Если вы это сделаете, поместите все выражение, заканчивающееся оператором постфикса, в круглых скобках.

Фактически, начиная с Scala 2.10.0, при этом по умолчанию генерируется предупреждение.

Для удобства вы можете переместить оператор postfix и использовать для него точечную нотацию. Например:

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

Или, еще проще,

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

С другой стороны, обратите внимание на методы, где вы можете поставить пустые скобки, чтобы превратить их в инфикс:

fromFile(file) mkString () map caesar(k)_
2 голосов
/ 08 апреля 2011

Спецификация не проясняет, но мой опыт и эксперименты показали, что компилятор Scala всегда будет пытаться обрабатывать вызовы методов, используя инфиксную нотацию как инфиксные операторы.Несмотря на то, что вы используете mkString в качестве постфикса, компилятор пытается интерпретировать его как инфикс, и поэтому пытается интерпретировать «карту» в качестве аргумента.За всеми использованиями постфиксных операторов должен либо сразу следовать терминатор выражения, либо он должен использоваться с «точечной» нотацией, чтобы компилятор мог видеть его таким образом.

Вы можете получить подсказку об этом (хотя это и не прописано)) в Путешествие по Скала: Операторы .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...