В чем разница между "def" и "val" для определения функции

В чем разница между:

def even: Int => Boolean = _ % 2 == 0

и

val even: Int => Boolean = _ % 2 == 0

Оба могут называться как even(10).

Комментарии к вопросу (5)
Решение

Метод def even оценивается при вызове и каждый раз создает новую функцию (новый экземпляр Function1).

def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false

val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

С помощью def вы можете получать новую функцию при каждом вызове:

val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1049057402
test()
// Int = -1049057402 - same result

def test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -240885810
test()
// Int = -1002157461 - new result

val оценивает при определении, def - при вызове:

scala> val even: Int => Boolean = ???
scala.NotImplementedError: an implementation is missing

scala> def even: Int => Boolean = ???
even: Int => Boolean

scala> even
scala.NotImplementedError: an implementation is missing

Обратите внимание, что существует и третий вариант: lazy val.

Он оценивается при первом вызове:

scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = 

scala> even
scala.NotImplementedError: an implementation is missing

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

lazy val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true

lazy val test: () => Int = {
  val r = util.Random.nextInt
  () => r
}

test()
// Int = -1068569869
test()
// Int = -1068569869 - same result

Производительность

val оценивает, когда определено.

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

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

Как отметил @SargeBorsch, вы можете определить метод, и это самый быстрый вариант:

def even(i: Int): Boolean = i % 2 == 0

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

Комментарии (10)

Подумайте об этом:

scala> def even: (Int => Boolean) = {
             println("def"); 
             (x => x % 2 == 0)
       }
even: Int => Boolean

scala> val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }
val //gets printed while declaration. line-4
even2: Int => Boolean = 

scala> even(1)
def
res9: Boolean = false

scala> even2(1)
res10: Boolean = false

Видите разницу? Вкратце:

def: Для каждого вызова even, он снова вызывает тело метода even. А в случае с even2, то есть val, функция инициализируется только один раз при объявлении (и поэтому печатает val в строке 4 и больше никогда) и при каждом обращении используется один и тот же вывод. Например, попробуйте сделать следующее:

scala> import scala.util.Random
import scala.util.Random

scala> val x = { Random.nextInt }
x: Int = -1307706866

scala> x
res0: Int = -1307706866

scala> x
res1: Int = -1307706866

Когда x инициализируется, значение, возвращаемое Random.nextInt, устанавливается в качестве конечного значения x. В следующий раз, когда x будет использоваться снова, он всегда будет возвращать одно и то же значение.

Можно также лениво инициализировать x. То есть при первом использовании он инициализируется, а не во время объявления. Например:

scala> lazy val y = { Random.nextInt }
y: Int = 

scala> y
res4: Int = 323930673

scala> y
res5: Int = 323930673
Комментарии (4)

См. это:

  var x = 2 // using var as I need to change it to 3 later
  val sq = x*x // evaluates right now
  x = 3 // no effect! sq is already evaluated
  println(sq)

Удивительно, но это выведет 4, а не 9! val (даже var) оценивается сразу и присваивается.
Теперь замените val на def... будет выведено 9! Def - это вызов функции. Она будет оцениваться каждый раз, когда ее вызывают.

Комментарии (0)

Валь, т. е. на "ьquot кв.&; по определению Scala является фиксированной. Она оценивается прямо во время объявления, вы можете't изменить позже. В других примерах, где even2 также Вэл, но она заявила, с функцией подписи, т. е. на "(Инт => логическое значение) - то", так это не типа int. Это функция и она'значение определяется следующим выражением

   {
         println("val");
         (x => x % 2 == 0)
   }

Согласно Скала собственность Валь, вы можете'т назначить другую функцию для even2, же правило, как КВ.

О том, почему вызов eval2 Валь функция не печатать на "Вэл" снова и снова ?

Код ориг:

val even2: (Int => Boolean) = {
             println("val");
             (x => x % 2 == 0)
       }

Мы знаем, в последнем заявлении Скала выше своеобразное выражение (внутри { .. }) фактически вернуться к левой стороне. Так что вы в конечном итоге установка even2, чтобы "Х => х % 2 == 0 и" функция, которая совпадает с типом вы объявили для even2 вал типа т. е. (Инт => булево), так что компилятор счастлив. Теперь even2 только очков, чтобы "(Х => х % 2 == 0)" по функции (не любое другое заявление, т. е. код println (на"вал", У) и т. д. Вызов event2 с разными параметрами будет на самом деле ссылаться на "(Х => х % 2 == 0)" и код, а только то, что сохраняется с event2.

scala> even2(2)
res7: Boolean = true

scala> even2(3)
res8: Boolean = false

Просто для уточнения более этого, следующим разные версии кода.

scala> val even2: (Int => Boolean) = {
     |              println("val");
     |              (x => { 
     |               println("inside final fn")
     |               x % 2 == 0
     |             })
     |        }

Что будет ? здесь мы видим, что "внутри окончательной ФН" и печатается снова и снова, когда вы называете even2().

scala> even2(3)
inside final fn
res9: Boolean = false

scala> even2(2)
inside final fn
res10: Boolean = true

scala> 
Комментарии (0)

Выполнение определения, такие как деф х = е не будет оценивать выражение e. В - Стед e вычисляется всякий раз, когда х вызывается.

Кроме того, Scala предлагает определение стоимости Валь X = Е,который оценит правой стороне в рамках оценки определение. Если x затем используется, он немедленно заменяется на предварительно вычисленные значения е, так что выражение не следует снова оценить.

Комментарии (0)

В REPL,

scala> def even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean

scala> val even: Int => Boolean = { _% 2 == 0 }
even: Int => Boolean = $$Lambda$1157/1017502292@57a0aeb8

вызов по имени Def означает``, оценивали по требованию

вызов по значению Валь означает``, оценивается в процессе инициализации

Комментарии (1)

В дополнение к вышесказанному полезные ответы, мои выводы:

def test1: Int => Int = {
x => x
}
--test1: test1[] => Int => Int

def test2(): Int => Int = {
x => x+1
}
--test2: test2[]() => Int => Int

def test3(): Int = 4
--test3: test3[]() => Int

Из вышесказанного видно, что “деф” - это метод (с нулевыми параметрами аргументов), которая возвращает другую функцию "и Инт => Инт” при вызове.

Преобразование методы функции хорошо описаны здесь: https://tpolecat.github.io/2014/06/09/methods-functions.html

Комментарии (0)

кроме того, Валь-это оценка стоимости. Что означает, что правая часть выражения вычисляется при определении. Где Дэф-оценка имя. Он не будет оценивать, пока он's используемый.

Комментарии (0)