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