В чем разница между "def" и " val " для определения функции
в чем разница между:
def even: Int => Boolean = _ % 2 == 0
и
val even: Int => Boolean = _ % 2 == 0
как можно назвать как even(10)
.
7 ответов:
метод
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 = <lazy> 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
, так что вы можете определить его, даже если вы не будете использовать его в некоторых ветвях.С
lazy val
вы получите ленивую оценку: вы можно определить его, даже если вы не будете использовать его в некоторых ветвях, и он оценивает один раз или никогда, но вы получите немного накладных расходов от двойной проверки блокировки каждого доступа к вашемуlazy val
.как отметил @ SargeBorsch, вы можете определить метод, и это самый быстрый вариант:
def even(i: Int): Boolean = i % 2 == 0
но если вам нужна функция (не метод) для композиции функций или для функций более высокого порядка (например,
filter(even)
) компилятор будет генерировать функцию из вашего метода каждый раз, когда вы используя его как функцию, так что производительность может быть немного хуже, чем сval
.
рассмотрим следующий пример:
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 = <function1> scala> even(1) def res9: Boolean = false scala> even2(1) res10: Boolean = false
вы видите разницу? Короче говоря:
def: для каждого вызова
even
, он называет телоeven
метод снова. Но сeven2
т. е. вал, функция инициализируется только один раз во время объявления (и, следовательно, он печатает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
is инициализировано, значение, возвращаемоеRandom.nextInt
устанавливается как конечное значениеx
. В следующий разx
используется снова, он всегда будет возвращать то же значение.вы также можете лениво инициализировать
x
. т. е. первый раз, когда он используется, он инициализируется, а не во время объявления. Например:scala> lazy val y = { Random.nextInt } y: Int = <lazy> scala> y res4: Int = 323930673 scala> y res5: Int = 323930673
это:
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-это вызов функции.. он будет оценивать каждый раз, когда он вызывается.
val т. е." sq " по определению Scala является фиксированным. Он оценивается прямо во время объявления, вы не можете изменить позже. В других примерах, где even2 также val, но он объявлен с сигнатурой функции, т. е. "(Int => Boolean)", поэтому он не является типом Int. Это функция, и ее значение задается следующим выражением
{ println("val"); (x => x % 2 == 0) }
согласно свойству Scala val, вы не можете назначить другую функцию even2, то же правило, что и sq.
о том, почему вызов функции eval2 val не печать "val" снова и снова ?
ориг код:
val even2: (Int => Boolean) = { println("val"); (x => x % 2 == 0) }
мы знаем, что в Scala последнее утверждение вышеприведенного вида выражения (inside { .. }) фактически возвращается в левую сторону. Таким образом, вы в конечном итоге устанавливаете even2 в функцию "x => x % 2 == 0", которая соответствует типу, объявленному для типа even2 val, т. е. (Int => Boolean), поэтому компилятор счастлив. Теперь even2 указывает только на функцию" (x => x % 2 == 0)"(не любое другое утверждение перед т. е. println ("val") и т. д. Вызов event2 с различными параметрами будет фактически вызывать код" (x => x % 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 | }) | }
что будет ? здесь мы видим, что "inside final fn" печатается снова и снова, когда вы вызываете even2().
scala> even2(3) inside final fn res9: Boolean = false scala> even2(2) inside final fn res10: Boolean = true scala>
кроме того, Val-это оценка по значению. Это означает, что правостороннее выражение вычисляется во время определения. Где Def-это оценка по имени. Он не будет оценивать, пока он не будет использован.
выполнение определения типа
def x = e
не будет вычислять выражение e. вместо e вычисляется всякий раз, когда вызывается x.кроме того, Scala предлагает определение стоимости
val x = e
, который оценивает правую сторону как часть оценки определения. Если затем используется x, то он немедленно заменяется предварительно вычисленным значением e, так что выражение не нужно вычислять снова.
в дополнение к приведенным выше полезным ответам, мои выводы:
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
выше показано, что" def "- это метод (с нулевыми параметрами аргумента), который возвращает другую функцию" Int => Int " при вызове.
преобразование методов в функции хорошо объясняется здесь:https://tpolecat.github.io/2014/06/09/methods-functions.html