В чем разница между "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, когда
xis инициализировано, значение, возвращаемое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