Яка різниця між:
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, тому ви можете визначити це, навіть якщо ви не будете використовувати його в деяких галузях.
З 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тобто 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 (навіть вар) оцінюється негайно і присвоюється.
Тепер змініть 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)" (не будь-яка інша заява перед ie 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
| })
| }
Що станеться ? тут ми бачимо, що "всередині остаточного 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. Замість е оцінюється кожен раз, коли викликається х.
Крім того, Scala пропонує визначення значення
val x = e, яке оцінює праву частину як частину оцінки визначення. Якщо x потім використовується згодом, його негайно замінюють заздалегідь обчисленим значенням e, так що вираз не потрібно оцінювати знову.
також Val - це оцінка за вартістю. Що означає вираз правого боку оцінюється під час визначення. Де Деф - за оцінкою імен. Він не буде оцінювати, поки він не буде використаний.
Крім вищезгаданих корисних відповідей, мої висновки:
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