Какова формальная разница в Scala между фигурными скобками и скобками, и когда они должны использоваться?



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



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



например (просто подразумевается в качестве примера; я был бы признателен за любой ответ, который обсуждает общий случай, а не этот конкретный только пример):



val tupleList = List[(String, String)]()
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 )


=> ошибка: незаконное начало простого выражения



val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }


=> штраф.

686   8  

8 ответов:

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

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

List(1, 2, 3).reduceLeft{_ + _} // valid, single Function2[Int,Int] parameter

List{1, 2, 3}.reduceLeft(_ + _) // invalid, A* vararg parameter

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

увеличена проверка компиляции с помощью parens

авторы спрея рекомендуют круглые парены, потому что они дают повышенную проверку компиляции. Это особенно важно для предметно-ориентированных языков, такие как спрей. Используя parens, вы говорите компилятору, что ему нужно дать только одну строку; поэтому, если вы случайно дадите ему две или более, он будет жаловаться. Теперь это не так с фигурными скобками – если, например, вы забыли оператор где-то, то ваш код будет компилироваться, и вы получите неожиданные результаты и потенциально очень трудно найти ошибку. Ниже придумано (так как выражения чисты и по крайней мере дадут предупреждение), но делает точку:

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
)

первый компилирует, второй дает error: ')' expected but integer literal found. Автор хотел написать 1 + 2 + 3.

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

многословие

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

... закрывающая скобка находится на отдельной строке сразу после последней строка функции.

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

Инфиксной Нотации

при использовании инфиксной нотации, как List(1,2,3) indexOf (2) вы можете опустить скобки, если есть только один параметр и записать его как List(1, 2, 3) indexOf 2. Это не относится к точечной нотации.

обратите внимание также, что когда у вас есть один параметр, который является выражением с несколькими токенами, например x + 2 или a => a % 2 == 0, вы должны использовать скобки для укажите границы выражения.

ОК

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

функция/частичные литералы функции с case

Scala имеет синтаксис для литералов функций и частичных функций. Выглядит это так:

{
    case pattern if guard => statements
    case pattern => statements
}

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

object match {
    case pattern if guard => statements
    case pattern => statements
}
try {
    block
} catch {
    case pattern if guard => statements
    case pattern => statements
} finally {
    block
}

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

выражения и блоки

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

{
    import stuff._
    statement ; // ; optional at the end of the line
    statement ; statement // not optional here
    var x = 0 // declaration
    while (x < 10) { x += 1 } // stuff
    (x % 5) + 1 // expression
}

( expression )

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

( { var x = 0; while (x < 10) { x += 1}; x } % 5) + 1

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

1       // literal
(1)     // expression
{1}     // block of code
({1})   // expression with a block of code
{(1)}   // block of code with an expression
({(1)}) // you get the drift...

где они не заменимы

в принципе, вы не можете заменить {} С () или наоборот в другом месте. Например:

while (x < 10) { x += 1 }

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

while ({x < 10}) { (x += 1) }

Итак, я надеюсь, что это помогает.

здесь есть несколько разных правил и выводов: прежде всего, Scala выводит фигурные скобки, когда параметр является функцией, например, в list.map(_ * 2) фигурные скобки выводятся, это просто более короткая форма list.map({_ * 2}). Во-вторых, Scala позволяет пропустить скобки в последнем списке параметров, если этот список параметров имеет один параметр и является функцией, поэтому list.foldLeft(0)(_ + _) можно записать как list.foldLeft(0) { _ + _ } (или list.foldLeft(0)({_ + _}) если вы хотите быть более явным).

однако, если вы добавите case вы получаете, как уже упоминалось, частичную функцию вместо функции, и Scala не будет выводить фигурные скобки для частичных функций, поэтому list.map(case x => x * 2) не будет работать, а как list.map({case x => 2 * 2}) и list.map { case x => x * 2 } будет.

сообщество пытается стандартизировать использование фигурных скобок и скобок, см. Руководство по стилю Scala (стр. 21):http://www.codecommit.com/scala-style-guide.pdf

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

val filtered = tupleList takeWhile { case (s1, s2) => s1 == s2 }

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

val result = myInstance.foo(5, "Hello")

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

имея, что мы можем делать такие вещи, как:

2 + { 3 }             // res: Int = 5
val x = { 4 }         // res: x: Int = 4
List({1},{2},{3})     // res: List[Int] = List(1,2,3)

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

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

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }

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

foo( x => println(x) )

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

foo({ x => println(x) })

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

но мы можем добавить еще кое-что:

foo({ println("Hey"); x => println(x) })

теперь наш блок кода содержит два оператора, и поскольку он оценивается до выполнения foo, происходит то, что сначала печатается "Hey", затем наша функция передается foo, печатается "ввод foo" и, наконец," 4" напечатанный.

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

foo { println("Hey"); x => println(x) }

или

foo { x => println(x) }

это выглядит намного лучше и эквивалентно предыдущим. Здесь еще блок кода вычисляется первым и результат вычисления (который является x => println(x)) передается в качестве аргумента foo.

Я не думаю, что есть что-то особенное или сложное о фигурных скобках в Scala. Чтобы освоить кажущееся сложным использование их в Scala, просто имейте в виду несколько простых вещей:

  1. фигурные скобки образуют блок кода, который вычисляется до последней строки кода (почти все языки делают это)
  2. функция при желании может быть сгенерирована с блоком кода (следует правилу 1)
  3. фигурные скобки могут быть опущены для однострочного кода, за исключением предложение case (выбор Scala)
  4. скобки могут быть опущены в вызове функции с блоком кода в качестве параметра (выбор Scala)

давайте объясним пару примеров в соответствии с приведенными выше тремя правилами:

val tupleList = List[(String, String)]()
// doesn't compile, violates case clause requirement
val filtered = tupleList.takeWhile( case (s1, s2) => s1 == s2 ) 
// block of code as a partial function and parentheses omission,
// i.e. tupleList.takeWhile({ case (s1, s2) => s1 == s2 })
val filtered = tupleList.takeWhile{ case (s1, s2) => s1 == s2 }

// curly braces omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft(_+_)
// parentheses omission, i.e. List(1, 2, 3).reduceLeft({_+_})
List(1, 2, 3).reduceLeft{_+_}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).reduceLeft _+_ // res1: String => String = <function1>

// curly braces omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0)(_ + _)
// parentheses omission, i.e. List(1, 2, 3).foldLeft(0)({_ + _})
List(1, 2, 3).foldLeft(0){_ + _}
// block of code and parentheses omission
List(1, 2, 3).foldLeft {0} {_ + _}
// not both though it compiles, because meaning totally changes due to precedence
List(1, 2, 3).foldLeft(0) _ + _
// error: ';' expected but integer literal found.
List(1, 2, 3).foldLeft 0 (_ + _)

def foo(f: Int => Unit) = { println("Entering foo"); f(4) }
// block of code that just evaluates to a value of a function, and parentheses omission
// i.e. foo({ println("Hey"); x => println(x) })
foo { println("Hey"); x => println(x) }

// parentheses omission, i.e. f({x})
def f(x: Int): Int = f {x}
// error: missing arguments for method f
def f(x: Int): Int = f x

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

увеличена проверка компиляции с помощью parens

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

method {
  1 +
  2
  3
}

method(
  1 +
  2
  3
 )

первый компилирует, второй дает error: ')' expected but integer literal found. автор хотел написать 1 + 2 + 3.

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

многословие

важное часто упускаемое из виду замечание о многословии. Использование фигурных скобок неизбежно приводит к подробному коду, поскольку руководство по стилю scala четко указывает, что закрытие фигурных скобок должно быть на их собственной строке:http://docs.scala-lang.org/style/declarations.html"... закрывающая скобка находится на своей собственной линии сразу после последней строки функции."Многие автореформаторы, как и в Intellij, будут автоматически выполните это переформатирование для вас. Поэтому старайтесь придерживаться использования круглых пар, когда вы можете. Е. Г. List(1, 2, 3).reduceLeft{_ + _} будет:

List(1, 2, 3).reduceLeft {
  _ + _
}

с фигурными скобками, вы получили точку с запятой индуцированной для вас и скобки нет. Рассмотрим takeWhile функция, так как она ожидает частичную функцию, только {case xxx => ??? } является допустимым определением вместо круглых скобок вокруг выражения case.

Comments

    Ничего не найдено.