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

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

Например,

(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 типа Опция [Список [com.sharca.Presentation]] не принимает параметры"

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

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

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

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

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

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

Ответы

Ответ 1

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

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

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

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

class EmptyClass

Создание экземпляра класса:

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

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

Это даст вам ошибку типа (Int found, expected String)

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

В то время как это прекрасно работает:

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

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


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

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

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

def returnAString = "Hi!"

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

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

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

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

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

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

() фактически не отбрасывается сам по себе при определении pass by name paramenter, но на самом деле это довольно семантически различная нотация, то есть

def myOp(passByNameString: => String)

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

def myOp(functionParam: () => String)

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

(Имейте в виду, что параметры pass-by-name скомпилируются в функции, а просто улучшают синтаксис.)

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

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

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

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

Заявление:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 2

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

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

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

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

Префикс:

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

Infix:

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

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

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

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

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

То же самое, что:

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

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

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

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

Curried функции трудно использовать с нотами infix. Ярким примером этого являются функции сгибания:

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

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

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

 list tail map (...)

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

 list tail length

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

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

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

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

Ответ 3

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

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

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

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

Из глава 2, "Тип меньше, больше" , программирования Scala:

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

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

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

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

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

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

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

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

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

Из сообщения блога Scala Синтаксис Primer:

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

Пример 4.6.3. Вот декларация и определение процедуры, названной написать:

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 позволяет разработчику заменить. с пробелом и пропустить круглые скобки, позволяющие оператору синтаксис, показанный нашим оператором вставки пример. Этот синтаксис используется в других места в API Scala, например создание экземпляров Range:

val firstTen:Range = 0 to 9

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

От Scala для Java Refugees Часть 6: Получение Java > :

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

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

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

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

Но меня тоже смущает эта цитата:

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

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

Ответ 4

Собственно, во втором чтении, возможно, это ключ:

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

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

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

1 + 2
1.+(2)

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

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

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

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

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

  • Если в выражении имеется несколько операций инфикса, то операторы с более высоким приоритетом связывают более тесно, чем операторы с более низкими старшинство.
  • Если есть последовательные операции инфикса e0 op1 e1 op2.,.opn en с операторами op1,.,, opn одинаковый приоритет, то все эти операторы должны иметь одинаковые ассоциативность. Если все операторы лево-ассоциативный, последовательность интерпретируется как (... (e0 op1 e1) op2,.) opn en. В противном случае, если все операторы являются rightassociative, последовательность интерпретируется как e0 op1 (e1 op2 (.opn en).,.).
  • Операторы Postfix всегда имеют более низкий приоритет, чем операторы infix. Например. e1 op1 e2 op2 всегда эквивалентен (e1 op1 e2) op2.

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

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

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

Ответ 5

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

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

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

Ответ 6

Мне легче следовать этому правилу: в пространствах выражений чередуются методы и параметры. В вашем примере (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.