Каковы точные правила, когда вы можете опустить круглые скобки, точки, фигурные скобки, = (функции) и т. Д.?

Каковы точные правила, когда вы можете опустить (опустить) скобки, точки, фигурные скобки, = (функции) и т. Д.?

Например,

(service.findAllPresentations.get.first.votes.size) must be equalTo(2).
  • service мой объект
  • def findAllPresentations: Option[List[Presentation]]
  • votes возвращается List[Vote]
  • должны и быть обе функции спецификации

Почему я не могу пойти:

(service findAllPresentations get first votes size) must be equalTo(2)

?

Ошибка компилятора:

"RestServicesSpecTest.this.service.findAllPresentations типа Option[List[com.sharca.Presentation]] не принимает параметры"

Почему кажется, что я пытаюсь передать параметр? Почему я должен использовать точки для каждого вызова метода?

Почему должен (service.findAllPresentations get first votes size) быть равным (2) результат:

"не найдено: сначала значение"

Тем не менее, "должен быть равен 2"(service.findAllPresentations.get.first.votes.size) должно быть равно 2, то есть метод цепочки работает нормально? - объект цепочка цепочка парам.

Я просмотрел книгу и сайт Scala и не могу найти исчерпывающего объяснения.

На самом ли деле, как объясняет Роб Х в вопросе переполнения стека, какие символы я могу опустить в Scala?, что единственный допустимый вариант использования для пропуска '.' предназначен для операций в стиле "оператор-операнд", а не для цепочки методов?

6 ответов

Решение

Определения классов:

val или же var может быть опущено из параметров класса, что сделает параметр закрытым.

Добавление var или val сделает его общедоступным (то есть генерируются методы доступа и мутаторы).

{} может быть опущен, если у класса нет тела, то есть

class EmptyClass

Классовое создание:

Общие параметры могут быть опущены, если они могут быть определены компилятором. Однако обратите внимание, что если ваши типы не совпадают, то параметр type всегда выводится так, чтобы он соответствовал. Таким образом, без указания типа, вы можете не получить то, что вы ожидаете - то есть, учитывая

class D[T](val x:T, val y:T);

Это даст вам ошибку типа (Int найдено, ожидаемая строка)

var zz = new D[String]("Hi1", 1) // type error

Тогда как это работает нормально:

var z = new D("Hi1", 1)
== D{def x: Any; def y: Any}

Поскольку параметр типа T выводится как наименее распространенный супертип из двух - Any.


Определения функций:

= может быть отброшен, если функция возвращает единицу (ничего).

{} для тела функции может быть удалено, если функция является одним оператором, но только если оператор возвращает значение (вам нужно = знак), то есть

def returnAString = "Hi!"

но это не работает:

def returnAString "Hi!" // Compile error - '=' expected but string literal found."

Возвращаемый тип функции может быть опущен, если он может быть выведен (рекурсивный метод должен иметь свой тип возвращаемого значения).

() может быть отброшен, если функция не принимает никаких аргументов, то есть

def endOfString {
  return "myDog".substring(2,1)
}

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

() фактически не отбрасывается как таковой при определении параметра передачи по имени, но на самом деле это совершенно семантически другое обозначение, то есть

def myOp(passByNameString: => String)

Говорит, что myOp принимает параметр pass-by-name, что приводит к String (то есть это может быть блок кода, который возвращает строку), а не к параметрам функции,

def myOp(functionParam: () => String)

который говорит myOp принимает функцию с нулевыми параметрами и возвращает строку.

(Имейте в виду, параметры передачи по имени скомпилированы в функции; это только делает синтаксис более приятным.)

() может быть отброшен в определении параметра функции, если функция принимает только один аргумент, например:

def myOp2(passByNameString:(Int) => String) { .. } // - You can drop the ()
def myOp2(passByNameString:Int => String) { .. }

Но если требуется более одного аргумента, вы должны включить ():

def myOp2(passByNameString:(Int, String) => String) { .. }

Заявления:

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

  • . также может быть отброшен для хвоста списка постфиксных функций

  • () может быть отброшено для постфиксных операторов list.tail

  • () не может использоваться с методами, определенными как:

    def aMethod = "hi!" // Missing () on method definition
    aMethod // Works
    aMethod() // Compile error when calling method
    

Поскольку эта нотация зарезервирована по соглашению для методов, которые не имеют побочных эффектов, таких как List#tail (то есть вызов функции без побочных эффектов означает, что функция не имеет наблюдаемого эффекта, за исключением ее возвращаемого значения).

  • () может быть отброшен для обозначения оператора при передаче одного аргумента

  • () может потребоваться использовать постфиксные операторы, которые не находятся в конце оператора

  • () может потребоваться для обозначения вложенных операторов, концов анонимных функций или для операторов, которые принимают более одного параметра

При вызове функции, которая принимает функцию, вы не можете опустить () из определения внутренней функции, например:

def myOp3(paramFunc0:() => String) {
    println(paramFunc0)
}
myOp3(() => "myop3") // Works
myOp3(=> "myop3") // Doesn't work

При вызове функции, которая принимает параметр по имени, вы не можете указать аргумент как анонимную функцию без параметров. Например, учитывая:

def myOp2(passByNameString:Int => String) {
  println(passByNameString)
}

Вы должны назвать это как:

myOp("myop3")

или же

myOp({
  val source = sourceProvider.source
  val p = myObject.findNameFromSource(source)
  p
})

но нет:

myOp(() => "myop3") // Doesn't work

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

(Хорошо, в стремлении составить более полный и краткий ответ (если я что-то пропустил, или что-то не так / неточно, пожалуйста, прокомментируйте), я добавил к началу ответа. Обратите внимание, что это не язык спецификация, поэтому я не пытаюсь сделать это точно академически правильным - просто больше похоже на справочную карту.)

Вы, кажется, наткнулись на ответ. В любом случае, я постараюсь прояснить это.

Вы можете опустить точку при использовании префиксной, инфиксной и постфиксной нотации - так называемой нотации оператора. При использовании нотации оператора, и только потом, вы можете опустить круглые скобки, если в метод передано менее двух параметров.

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

Я кратко опишу обозначения.

Префикс:

Только ~, !, + а также - может использоваться в префиксной нотации. Это обозначение, которое вы используете при написании !flag или же val liability = -debt,

Infix:

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

Постфикс (также суффикс):

Эта нотация используется, когда метод следует за объектом и не получает параметров. Например, вы можете написать list tailи это запись постфикса.

Вы можете связать вызовы инфиксной нотации без проблем, если метод не каррируется. Например, мне нравится использовать следующий стиль:

(list
 filter (...)
 map (...)
 mkString ", "
)

Это то же самое, что и:

list filter (...) map (...) mkString ", "

Теперь, почему я использую скобки здесь, если фильтр и карта принимают один параметр? Это потому, что я передаю им анонимные функции. Я не могу смешать определения анонимных функций со стилем инфикса, потому что мне нужна граница для конца моей анонимной функции. Кроме того, определение параметра анонимной функции может интерпретироваться как последний параметр в методе infix.

Вы можете использовать инфикс с несколькими параметрами:

string substring (start, end) map (_ toInt) mkString ("<", ", ", ">")

Карри функции трудно использовать с инфиксной нотацией. Функции сворачивания являются ярким примером этого:

(0 /: list) ((cnt, string) => cnt + string.size)
(list foldLeft 0) ((cnt, string) => cnt + string.size)

Вам нужно использовать круглые скобки вне вызова инфикса. Я не уверен, точные правила игры здесь.

Теперь поговорим о постфиксе. Postfix может быть сложным в использовании, потому что он никогда не может использоваться где-либо, кроме конца выражения. Например, вы не можете сделать следующее:

 list tail map (...)

Потому что хвост не появляется в конце выражения. Вы также не можете сделать это:

 list tail length

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

 (list tail) map (...)
 (list tail) length

Обратите внимание, что постфиксная запись не рекомендуется, поскольку она может быть небезопасной.

Я надеюсь, что это очистило все сомнения. Если нет, просто оставьте комментарий, и я посмотрю, что я могу сделать, чтобы улучшить его.

Коллекция цитат, дающих представление о различных условиях...

Лично я думал, что будет больше в спецификации. Я уверен, что должно быть, я просто не ищу правильные слова...

Однако есть несколько источников, и я собрал их вместе, но ничего по-настоящему полного / всеобъемлющего / понятного /, который бы объяснил мне вышеупомянутые проблемы...:

"Если тело метода имеет более одного выражения, вы должны заключить его в фигурные скобки {…}. Вы можете опустить фигурные скобки, если тело метода имеет только одно выражение".

Из главы 2 "Введите меньше, сделайте больше", программирования Scala:

"Тело верхнего метода следует после знака равенства '='. Почему знак равенства? Почему бы не просто фигурные скобки {…}, как в Java? Потому что точки с запятой, возвращаемые типы функций, списки аргументов метода и даже фигурные скобки иногда опускаются, использование знака равенства предотвращает несколько возможных синтаксических различий.Использование знака равенства также напоминает нам, что четные функции являются значениями в Scala, что согласуется с поддержкой Scala функционального программирования, более подробно описанной в главе 8, Функциональное программирование в Скала ".

Из главы 1 " От нуля до шестидесяти: введение в Scala",программирования Scala:

"Функция без параметров может быть объявлена ​​без скобок, и в этом случае она должна вызываться без скобок. Это обеспечивает поддержку принципа унифицированного доступа, так что вызывающая сторона не знает, является ли символ переменной или функцией без параметры.

Тело функции предшествует "=", если оно возвращает значение (т. Е. Тип возвращаемого значения отличается от Unit), но тип возвращаемого значения и "=" могут быть опущены, когда тип является Unit (т. Е. Выглядит как процедура в отличие от функции).

Брекеты вокруг тела не требуются (если тело представляет собой одно выражение); точнее, тело функции - это просто выражение, и любое выражение с несколькими частями должно быть заключено в фигурные скобки (выражение с одной частью может быть заключено в фигурные скобки)."

"Функции с нулевым или одним аргументом можно вызывать без точки и скобок. Но любое выражение может иметь круглые скобки, поэтому вы можете опустить точку и по-прежнему использовать скобки.

А поскольку вы можете использовать фигурные скобки везде, где можете использовать круглые скобки, вы можете опустить точку и вставить фигурные скобки, которые могут содержать несколько операторов.

Функции без аргументов могут быть вызваны без скобок. Например, функция length() в строке может быть вызвана как "abc".length, а не "abc".length(). Если функция представляет собой функцию Scala, определенную без скобок, то функция должна вызываться без скобок.

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

Из блога Scala Syntax Primer:

"Определение процедуры - это определение функции, в котором тип результата и знак равенства опущены; определяющее выражение должно быть блоком. Например, def f (ps) {stats} эквивалентно def f (ps): Unit = {stats}.

Пример 4.6.3. Вот объявление и определение процедуры с именем write:

trait Writer {
    def write(str: String)
}
object Terminal extends Writer {
    def write(str: String) { System.out.println(str) }
}

Приведенный выше код неявно дополняется следующим кодом:

trait Writer {
    def write(str: String): Unit
}
object Terminal extends Writer {
    def write(str: String): Unit = { System.out.println(str) }
}"

Из спецификации языка:

"С помощью методов, которые принимают только один параметр, Scala позволяет разработчику заменить символ. Пробелом и опустить скобки, включив синтаксис оператора, показанный в нашем примере оператора вставки. Этот синтаксис используется в других местах в Scala API, например как построение экземпляров Range:

val firstTen:Range = 0 to 9

Здесь снова, to(Int) - это ванильный метод, объявленный внутри класса (здесь на самом деле есть еще несколько неявных преобразований типов, но вы получаете дрейф)."

Из Scala для Java-беженцев. Часть 6. Преодоление Java:

"Теперь, когда вы пробуете"m 0", Scala отбрасывает его как унарный оператор на том основании, что он недопустим (~,!, - и +). Он обнаруживает, что" m "является допустимым объектом - это функция, а не метод, и все функции являются объектами.

Так как "0" не является допустимым идентификатором Scala, он не может быть ни инфиксным, ни постфиксным оператором. Поэтому Скала жалуется, что ожидал ";" - которые будут разделять два (почти) допустимых выражения: "m" и "0". Если вы вставите его, то будет жаловаться, что m требует либо аргумента, либо, в случае неудачи, "_", чтобы превратить его в частично примененную функцию ".

"Я считаю, что стиль синтаксиса оператора работает только тогда, когда у вас есть явный объект в левой части. Синтаксис предназначен для того, чтобы вы могли естественным образом выражать операции в стиле" оператор-операнд ".

Какие символы я могу опустить в Scala?

Но что меня смущает, так это цитата:

"Для получения вызова метода должен существовать объект. Например, вы не можете выполнить" println "Hello World!" ", Так как для println нужен получатель объекта. Вы можете сделать "Console println" Hello World! "," Которая удовлетворяет потребности ".

Потому что, насколько я вижу, есть объект для приема вызова...

Мне легче следовать этому правилу: в выражениях пробелы чередуются между методами и параметрами. В вашем примере (service.findAllPresentations.get.first.votes.size) must be equalTo(2) разбирает как (service.findAllPresentations.get.first.votes.size).must(be)(equalTo(2)), Обратите внимание, что круглые скобки вокруг 2 имеют более высокую ассоциативность, чем пробелы. Точки также имеют более высокую ассоциативность, поэтому (service.findAllPresentations.get.first.votes.size) must be.equalTo(2)будет разбирать как (service.findAllPresentations.get.first.votes.size).must(be.equalTo(2)),

service findAllPresentations get first votes size must be equalTo 2 разбирает как service.findAllPresentations(get).first(votes).size(must).be(equalTo).2,

Там нет ни одного. Скорее всего, вы получите совет относительно того, имеет ли функция побочные эффекты. Это фальшивка. Исправление заключается в том, чтобы не использовать побочные эффекты в разумных пределах, разрешенных Scala. Если это невозможно, все ставки отменены. Все ставки. Использование скобок является элементом набора "все" и является излишним. Он не предоставляет никакого значения, когда все ставки выключены.

Этот совет по сути является попыткой создания системы эффектов, которая выходит из строя (не путать с: менее полезна, чем другие системы эффектов).

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

На самом деле, во втором чтении, может быть, это ключ:

С помощью методов, которые принимают только один параметр, Scala позволяет разработчику заменить. с пробелом и опустить скобки

Как упомянуто в сообщении в блоге: http://www.codecommit.com/blog/scala/scala-for-java-refugees-part-6.

Так что, возможно, это на самом деле очень строгий "синтаксический сахар", который работает только тогда, когда вы эффективно вызываете метод для объекта, который принимает один параметр. например

1 + 2
1.+(2)

И ничего больше.

Это объяснило бы мои примеры в вопросе.

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

Хорошо, какой-то приятель (paulp_ из #scala) указал, где в спецификации языка эта информация находится:

6.12.3. Приоритетность и ассоциативность операторов определяют группировку частей выражения следующим образом.

  • Если в выражении несколько инфиксных операций, операторы с более высоким приоритетом связываются более тесно, чем операторы с более низким приоритетом.
  • Если есть последовательные операции инфикса e0 op1 e1 op2 .,.opn ru с операторами op1, .,, Если они имеют одинаковый приоритет, то все эти операторы должны иметь одинаковую ассоциативность. Если все операторы левоассоциативны, последовательность интерпретируется как (. . . (E0 op1 e1) op2 . .) Opn en. В противном случае, если все операторы прямоассоциативны, последовательность интерпретируется как e0 op1 (e1 op2 (. .Opn en) . .).
  • Постфиксные операторы всегда имеют более низкий приоритет, чем инфиксные операторы. Например, e1 op1 e2 op2 всегда эквивалентно (e1 op1 e2) op2.

Правый операнд левого ассоциативного оператора может состоять из нескольких аргументов, заключенных в скобки, например, e op (e1, . . .,en). Это выражение затем интерпретируется как e.op(e1, . . .,en).

Левоассоциативная двоичная операция e1 op e2 интерпретируется как e1.op(e2). Если op является прямоассоциативным, та же операция интерпретируется как { val x=e1; e2.op(x) }, где x - новое имя.

Хм - мне это не соответствует тому, что я вижу, или я просто не понимаю этого;)

Другие вопросы по тегам