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


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

например,

(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.этот.услуга.findAllPresentations типа Вариант[список[com.Шарка.Демонстрация]] не принимает параметры"

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

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

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

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

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

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

6 91

6 ответов:

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

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

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

я кратко детализирую обозначения.

префикс:

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

инфиксной:

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

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

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

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

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

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

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

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

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

class EmptyClass

инстанцирование класса:

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

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}

потому что параметр type, T, выводится как наименее общий супертип из двух - Любой.


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

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

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

def returnAString = "Hi!"

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

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

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

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

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

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

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

def myOp(passByNameString: => String)

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

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) { .. }

отчетность:

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

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

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

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

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

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

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

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

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

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

С Глава 2, "наберите меньше, сделайте больше", of Программирование Scala:

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

С Глава 1, "от нуля до шестидесяти: введение в Scala", of Программирование Скала:

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

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

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

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

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

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

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

в блоге Scala Syntax Primer:

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

пример 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, такие как построение экземпляров диапазона:

val firstTen:Range = 0 to 9

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

С Scala для беженцев Java Часть 6: преодоление Java:

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

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

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

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

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

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

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

на самом деле, при втором чтении, возможно, это ключ:

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

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

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

1 + 2
1.+(2)

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

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

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

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

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

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

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

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

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

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

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

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

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