Именованные параметры vs _, точечная запись vs инфиксная операция, фигурные vs круглые скобки при использовании функций высшего порядка в Scala

Мне очень трудно понять, когда я могу или не могу опускать скобки и/или точки, и как это взаимодействует с _.

Конкретный случай, который у меня был с этим, был

val x: X = ???
val xss: List[List[X]] = ???
xss map x :: _ //this doesn't compile
xss map _.::(x) //this is the same as the above (and thus doesn't compile)

два вышеупомянутых кажутся идентичными xss.map(_).::(x)

xss map (x :: _) //this works as expected
xss map {x :: _} //this does the same thing as the above

между тем, следующее также терпит неудачу:

xss.map xs => x :: xs //';' expected but '=>' found.
xss.map x :: _ //missing arguments for method map in class List; follow this method with `_' if you want to treat it as a partially applied function
//so when I try following the method with _, I get my favourite:
xss.map _ x :: _ //Cannot construct a collection of type That with elements of type B based on a collection of type List[List[Main.X]]
//as opposed to
xss map _ x :: _ //missing parameter type for expanded function ((x$1) => xss.map(x$1).x(($colon$colon: (() => <empty>))))

Прямо сейчас я часто играю в «переключение символов, пока он не скомпилируется», что я считаю неоптимальной стратегией программирования. Как это все работает?


person Martijn    schedule 18.10.2014    source источник
comment
Вы видели stackoverflow.com/questions/2173373/?   -  person kiritsuku    schedule 18.10.2014
comment
Еще нет, спасибо. Ищу.   -  person Martijn    schedule 18.10.2014
comment
Это должно помочь решить проблему, где опускать круглые скобки/точки: omit-parenthesis-dots-braces-f" title="каковы точные правила, когда вы можете опустить круглые скобки, точки, фигурные скобки, f"> stackoverflow.com/questions/1181533/   -  person kiritsuku    schedule 18.10.2014


Ответы (1)


Сначала нам нужно различать xss.map(f) и xss map f. Согласно документации Scala, любой метод, который принимает один параметр, может использоваться как инфиксный оператор.

На самом деле метод map в List является одним из этих методов. . Игнорируя полную подпись и тот факт, что она унаследована от TraversableLike, подпись выглядит следующим образом:

final def map[B](f: (A) ⇒ B): List[B]

Таким образом, он принимает один параметр, а именно f, который является функцией с типом A => B. Итак, если у вас есть значение функции, определенное как

val mySize = (xs:List[Int]) => xs.size

вы можете выбрать между

xss.map(mySize)

or

xss map mySize

Это вопрос предпочтений, но согласно Руководству по стилю Scala, в данном случае предпочтительнее последнее, если только оно не является частью сложного выражения, где лучше придерживаться записи через точку.

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

xss.map xs => x :: xs // Won't compile
xss.map x :: _ // Won't compile
xss.map _ x :: _ // Won't compile

Но в большинстве случаев вместо передачи значения функции вам нужно передать литерал функции (она же анонимная функция). В этом случае снова, если вы используете точечную нотацию, вам нужно что-то вроде xss.map(_.size). Но если вы используете инфиксную нотацию, это будет вопросом приоритета.

Например

xss map x :: _ // Won't compile!

не работает из-за приоритета оператора. Поэтому вам нужно использовать скобки, чтобы устранить неоднозначность ситуации для компилятора с помощью xss map (x :: _).

Использование фигурных скобок вместо скобок имеет очень ясное и простое правило. Опять же, любая функция, которая принимает только один параметр, может быть применена с фигурными скобками вместо скобок, как для инфиксной, так и для точечной нотации. Таким образом, следующие операторы будут скомпилированы.

xss.map{x :: _}
xss map {x :: _}

Чтобы избежать путаницы, вы можете начать с записи через точку и явных типов для параметров. Позже, после компиляции и, возможно, написания некоторых модульных тестов для вашего кода, вы можете начать рефакторинг кода, удалив ненужные типы, используя инфиксную нотацию и фигурные скобки вместо скобок, где это имеет смысл.

Для этого вы можете обратиться к Руководству по стилю Scala и выступление Мартина Одерски на Scala Days 2013, посвященное стилю кодирования Scala. Также вы всегда можете обратиться за помощью к IDE для рефакторинга кода, чтобы сделать его более кратким.

person Nader Ghanbari    schedule 19.10.2014
comment
отличный ответ, спасибо. Что касается запроса плагина Eclipse на рефакторинг моего кода - по моему опыту, это плохо кончается, хотя я хорошо отношусь к intellij - person Martijn; 19.10.2014
comment
Мне очень нравится плагин IntelliJ IDEA Scala. Он автоматически определяет части кода, которые могли бы быть проще. Например, если вы напишете xss map (mySize), будет предложено удалить круглые скобки. Даже если вы пишете xss map (mySize(_)), он предлагает сначала удалить ненужные _, а затем, после удаления этого, предлагает удалить скобки. Он достаточно умен, чтобы обнаруживать большинство распространенных проблем и запахов кода. - person Nader Ghanbari; 19.10.2014