Девять способов определения метода в Scala?
Итак, я пытался разгадать различные способы, которыми вы можете определить вещи в Scala, осложненные моим непониманием пути {}
блоки относятся:
object NewMain extends Thing{
def f1 = 10
def f2 {10}
def f3 = {10}
def f4() = 10
def f5() {10}
def f6() = {10}
def f7 = () => 10
def f8 = () => {10}
def f9 = {() => {10}}
def main(args: Array[String]){
println(f1) // 10
println(f2) // ()
println(f3) // 10
println(f4) // 10
println(f4()) // 10
println(f5) // ()
println(f5()) // ()
println(f6) // 10
println(f6()) // 10
println(f7) // <function0>
println(f7()) // 10
println(f8) // <function0>
println(f8()) // 10
println(f9) // <function0>
println(f9()) // 10
}
}
предположительно некоторые из них эквивалентны, некоторые из них являются синтаксическим сахаром для других, а некоторые вещи я не должен использовать, но я не могу для жизни меня понять это. Мои конкретные вопросы:
как это
println(f2)
иprintln(f5())
даетunit
? Это не последний элемент в блоке10
? Чем он отличается отprintln(f3())
, что дает10
?если
println(f5)
даетunit
, неprintln(f5())
недействительным, посколькуunit
это не функция? То же самое относится и кprintln(f6)
иprintln(f6())
из всех тех, которые печатают 10:
f1
,f3
,f4
,f4()
,f6
,f6()
,f7()
,f8()
,f9()
, есть ли функциональная разница между ними (в терминах что он делает) или различия в использовании (с точки зрения того, когда я должен использовать что)? Или все они эквивалентны?
4 ответа:
чтобы ответить на ваши вопросы по порядку:
f2
иf5()
returnUnit
потому что scala берет любойdef
без "=
" быть функцией, которая возвращаетUnit
, независимо от того, что последний элемент в блоке. Это хорошо, так как в противном случае было бы не слишком многословно определять функцию, которая ничего не возвращает.println(f5())
действителен, даже если он возвращаетUnit
потому что в scalaUnit
является допустимым объектом, хотя, по общему признанию, вы не можете создать экземпляр.Unit.toString()
является допустимым, если не вообще полезным, заявление, например.- не все версии, которые распечатывают
10
то же самое. Самое главное,f7
,f8
иf9
на самом деле функции, которые возвращают функции, которые возвращают10
, вместо того, чтобы возвратить10
напрямую. Когда вы объявляетеdef f8 = () => {10}
, вы объявляете функциюf8
который не принимает аргументов и возвращает функцию, которая не принимает аргументов и возвращает целое число. Когда вы вызываетеprintln(f8)
затемf8
dilligently возвращает вам эту функцию. Когда вы звонитеprintln(f8())
он возвращает функцию, а затем сразу же вызывает ее.- функции
f1
,f3
,f4
иf6
все они по существу эквивалентны с точки зрения того, что они делают, они различаются только с точки зрения стиля.как указывает "неизвестный пользователь", фигурные скобки важны только для целей определения области и не имеют никакого значения в вашем случае использования здесь.
def f() {...}
это сытактический сахар для
def f(): Unit = {...}
поэтому, если вы опустите"=", метод всегда будет возвращать объект типа Unit. В Scala методы и выражения всегда возвращают что-то.
def f() = 10 is sytactic sugar for def f() = { 10 }
если вы пишете def f () = () = > 10, это то же самое, что писать
def f() = { () => 10 }
таким образом, это означает, что f возвращает объект функции. Однако вы могли бы написать
val f = () => 10
когда вы вызываете это с помощью f () он возвращает 10 Функциональные объекты и методы может использоваться взаимозаменяемо в большинстве случаев, но есть несколько синтаксических различий. например. Когда вы пишете
def f() = 10 println(f)
вы получаете "10", но когда вы пишете
val f = () => 10 println(f)
вы получаете
<function0>
С другой стороны, когда у вас есть этот
val list = List(1,2,3) def inc(x: Int) = x+1 val inc2 = (x: Int) => x+1 println(list.map(inc)) println(list.map(inc2))
оба println будут печатать одно и то же
List(2,3,4)
при использовании имени метода в месте, где ожидается объект функции и сигнатура метода соответствует сигнатуре ожидаемый объект функции автоматически преобразуется. Так что
list.map(inc)
автоматически преобразуется компилятором scala вlist.map(x => inc(x))
шесть лет спустя, в будущей версии Scala, которая будет выпущена еще дальше в будущем, все улучшилось:
- определения
f2
иf5
были удалены как "синтаксис процедуры"- возможность вызова
f4
f5
иf6
без скобок удалены.это сводит наши 9 способов определения функции и 15 способов вызова их до 7 способов определения a функция и 10 способов их вызова:
object NewMain extends Thing{ def f1 = 10 def f3 = {10} def f4() = 10 def f6() = {10} def f7 = () => 10 def f8 = () => {10} def f9 = {() => {10}} def main(args: Array[String]){ println(f1) // 10 println(f3) // 10 println(f4()) // 10 println(f6()) // 10 println(f7) // <function0> println(f7()) // 10 println(f8) // <function0> println(f8()) // 10 println(f9) // <function0> println(f9()) // 10 } }
см. также lampepfl / dotty2570lampepfl / dotty#2571
в результате относительно ясно, какой синтаксис является необязательным (например,
{}
s) и какие определения эквивалентны (например,def f4() = 10
иdef f7 = () => 10
). Надеюсь, когда-нибудь, когда Dotty/Scala-3.0 будет выпущен, новички, изучающие язык, больше не будут сталкиваться с той же путаницей, что и шесть лет назад.
def f1 = 10 def f2 {10}
вторая форма не использует назначение. Поэтому вы можете думать об этом как о процедуре. Он не предназначен для возврата чего-то, и поэтому возвращает единицу, даже если последний оператор может быть использован для возврата чего-то конкретного (но это может быть оператор if, который будет иметь только что-то конкретное в одной ветви).
def f1 = 10 def f3 = {10}
вам не нужны скобки. Они нужны, например, если вы определяете val, поэтому область действия этого val ограничена вложением блок.
def sqrGtX (n:Int, x: Int) = { val sqr = n * n if (sqr > x) sqr / 2 else x / 2 }
вам нужны фигурные скобки, чтобы определить val sqr здесь. Если val объявлен во внутренней ветви, фигурные скобки не должны быть на верхнем уровне метода:
def foo (n:Int, x: Int) = if (n > x) { val bar = x * x + n * n println (bar) bar - 2 } else x - 2
для дальнейшего исследования, когда два метода возвращают один и тот же результат, вы можете скомпилировать их и сравнить байт-код. Будет два бинарные идентичные методы могут быть одинаковыми.