Девять способов определения метода в 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 55

4 ответа:

чтобы ответить на ваши вопросы по порядку:

  • f2 и f5() return Unit потому что scala берет любой def без "= " быть функцией, которая возвращает Unit, независимо от того, что последний элемент в блоке. Это хорошо, так как в противном случае было бы не слишком многословно определять функцию, которая ничего не возвращает.
  • println(f5()) действителен, даже если он возвращает Unit потому что в scala Unit является допустимым объектом, хотя, по общему признанию, вы не можете создать экземпляр. 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, которая будет выпущена еще дальше в будущем, все улучшилось:

это сводит наши 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 

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