В чем разница между:
def even: Int => Boolean = _ % 2 == 0
и
val even: Int => Boolean = _ % 2 == 0
Оба можно назвать как even(10)
.
В чем разница между:
def even: Int => Boolean = _ % 2 == 0
и
val even: Int => Boolean = _ % 2 == 0
Оба можно назвать как even(10)
.
Ответы:
Метод 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
, так что вы можете определить его, даже если вы не будете использовать его в некоторых ветвях.
С помощью a lazy val
вы получите ленивую оценку: вы можете определить ее, даже если вы не будете использовать ее в некоторых ветвях, и она будет оцениваться один или никогда, но вы получите небольшие издержки от двойной проверки блокировки при каждом доступе к вашему lazy val
.
Как заметил @SargeBorsch, вы можете определить метод, и это самый быстрый вариант:
def even(i: Int): Boolean = i % 2 == 0
Но если вам нужна функция (не метод) для композиции функций или для функций более высокого порядка (например filter(even)
), компилятор будет генерировать функцию из вашего метода каждый раз, когда вы используете ее как функцию, поэтому производительность может быть несколько хуже, чем с val
.
even
вызывается.
def
может использоваться для определения метода, и это самый быстрый вариант. @ A.Karimi
even eq even
.
@inline
атрибут для этого. Но он не может встроить функции, потому что вызов функции - это вызов виртуального apply
метода объекта функции. В некоторых ситуациях JVM может девиртуализировать и включать такие вызовы, но не в целом.
Учти это:
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
ie 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 = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
дважды, один раз с 1
и один раз с 2
. Вы получите разные ответы на каждый звонок. Таким образом, хотя при println
последующих вызовах функция не выполняется, вы не получите одинаковый результат от разных вызовов even2
. Что касается того, почему println
не выполняется снова, это другой вопрос.
Посмотри это:
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 последнее выражение вышеприведенного вида выражения (внутри {..}) фактически возвращается в левую часть. Таким образом, вы заканчиваете тем, что устанавливаете even2 в функцию «x => x% 2 == 0», которая соответствует типу, который вы объявили для типа Even2 val, т.е. (Int => Boolean), так что компилятор доволен. Теперь even2 указывает только на функцию «(x => x% 2 == 0)» (а не на любые другие операторы ранее, например println («val») и т. Д. Вызов события 2 с другими параметрами фактически вызовет «(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
| })
| }
Что случится ? здесь мы видим, что «внутри финального fn» печатается снова и снова, когда вы вызываете even2 ().
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Выполнение определения, такого как def x = e
, не будет оценивать выражение e. Вместо этого оценивается каждый раз, когда вызывается x.
В качестве альтернативы, Scala предлагает определение значения
val x = e
, которое оценивает правую часть как часть оценки определения. Если затем затем используется x, он немедленно заменяется предварительно вычисленным значением e, так что выражение не нужно вычислять снова.
также, Val является оценкой по значению. Это означает, что выражение правой части оценивается во время определения. Где Def по оценке имени. Он не будет оцениваться, пока не будет использован.
В дополнение к вышеупомянутым полезным ответам, мои выводы:
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
В 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 означает call-by-name
, оценивается по требованию
val означает call-by-value
, оценивается при инициализации
Int => Boolean
значит? Я думаю, что синтаксис определенияdef foo(bar: Baz): Bin = expr