Многострочный литерал функции в качестве аргументов в Scala
Я всегда задавался вопросом, почему иногда с литералами функций мы можем игнорировать фигурные скобки даже для нескольких операторов. Чтобы проиллюстрировать это, синтаксис для литерала многострочной функции заключается в заключении операторов с фигурными фигурными скобками. Таким образом,
val fl = (x: Int) => {
println("Add 25 to "+x)
x + 25
}
Однако, когда вы передаете его функции с одним аргументом, вы можете игнорировать требуемую фигурную скобку для литерала функции.
Итак, для данной функции f,
def f( fl: Int => Int ) {
println("Result is "+ fl(5))
}
Вы можете вызвать f() следующим образом:
f( x=> {
println("Add 25 to "+x)
x + 25
})
-------------------------
Add 25 to 5
Result: 30
Или, когда вы используете фигурные скобки вместо скобок в вызове функции, вы можете удалить внутренние фигурные фигурные скобки из литерала функции. Таким образом, следующий код также будет работать,
f{ x=>
println("Add 25 to "+x)
x + 25
}
Вышеприведенный код более читабельен, и я замечаю, что многие примеры используют этот синтаксис. Однако есть ли какое-то специальное правило, которое я, возможно, пропустил, чтобы объяснить, почему это работает по назначению?
Ответы
Ответ 1
Есть только несколько простых правил синтаксиса. Приложение спецификации заслуживает внимания.
Функциональная литеральная или анонимная функция (6.23) будет выглядеть как x => Expr
или x => Block
в зависимости от того, является ли контекст Expr или ResultExpr соответственно.
Приложение-функция (6.6) будет выглядеть как f(Expr, Expr)
или f BlockExpr
, т.е. f{ Block }
. То есть, BlockExpr - это всего лишь последовательность операторов блока внутри {...}
.
Когда вы вызываете f(g)
, тогда g является Expr, поэтому как литерал функции, x => Expr
. Expr может быть BlockExpr, x => { ... }
.
Когда вы вызываете f{ Block }
, тогда f { x => ... }
имеет литерал функции в ResultExpr блока (который представляет собой всего лишь последовательность инструкций, без привязок).
Здесь очевидно, что функция anon func находится в нижней части блока:
scala> def m(x: Int=>Int) = x(5)
m: (x: Int => Int)Int
scala> m {
| val y = 7
| x => // no brace
| x+y+1
| }
res0: Int = 13
Ответ 2
Это одна из вещей, которые делают Scala красивой для меня.
Простой ответ на ваш вопрос:
Круглые скобки() предназначены для однострочных конструкций. Например, это работает:
def f(fl: Int => Int) {
println("Result is " + fl(5))
}
f(
x =>
x + 25)
f(x => x + 25) // single line
и фигурные скобки {} предназначены для многострочных операторов. Например, это работает:
f {
x =>
println("Add 25 to " + x)
x + 25
}
но этот код не работает:
f (
x =>
println("Add 25 to " + x)
x + 25
)
Компилятор жалуется на следующее сообщение:
Значение x не является членом возможной причины Unit: возможно, точка с запятой отсутствует перед значением `x '?
Если вы добавите точку с запятой, вы получите синтаксическую ошибку, вызванную несогласованной скобкой.
Если вы попытаетесь сделать это:
f { x => println("Add 25 to " + x) x + 25 }
Компилятор вернется к вам с сообщением:
value x is not a member of unit
Вы понимаете, что он пытается найти x в качестве члена подразделения. Например:
f { println("Add 25 to " + x).x.+(25) }
Это явно неправильно.
Если вы добавите внутренние фигурные скобки, как это:
f (
x => {
println("Add 25 to " + x)
x + 25
}
)
Это также будет работать, но у вас все еще есть многострочный оператор, который сигнализируется использованием фигурных скобок. Поэтому компилятор знает, что вы хотите, чтобы сначала распечатать, а затем добавить 25 в x.
Я уже укусил эти тонкости. С тех пор я обращал внимание на то, как я кодирую их, потому что вы будете кодировать и читать много, когда вы в основном используете карты, плоские карты, foreachs, fors и currying.
Ура!