Arasındaki fark nedir:
def even: Int => Boolean = _ % 2 == 0
ve
val even: Int => Boolean = _ % 2 == 0
Her ikisi de gibi adlandırılabilir even(10)
.
Arasındaki fark nedir:
def even: Int => Boolean = _ % 2 == 0
ve
val even: Int => Boolean = _ % 2 == 0
Her ikisi de gibi adlandırılabilir even(10)
.
Yanıtlar:
Yöntem def even
çağrı üzerine değerlendirir ve her seferinde yeni işlev oluşturur (yeni örneği Function1
).
def even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = false
val even: Int => Boolean = _ % 2 == 0
even eq even
//Boolean = true
İle def
her çağrıda yeni bir işlev elde edebilirsiniz:
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
tanımlandığında değerlendirir, def
- çağrıldığında:
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
Üçüncü bir seçenek olduğunu unutmayın: lazy val
.
İlk çağrıldığında değerlendirir:
scala> lazy val even: Int => Boolean = ???
even: Int => Boolean = <lazy>
scala> even
scala.NotImplementedError: an implementation is missing
Ancak FunctionN
her seferinde aynı sonucu (bu durumda aynı örneğini ) döndürür :
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
Verim
val
tanımlandığında değerlendirir.
def
her çağrıda değerlendirilir, bu nedenle performans val
birden fazla çağrıdan daha kötü olabilir . Tek bir çağrı ile aynı performansı elde edersiniz. Ve hiçbir çağrı olmadan ek yük def
alamazsınız, böylece bazı şubelerde kullanmasanız bile tanımlayabilirsiniz.
Bir ile lazy val
bazı branşlarda bunu kullanmaz bile bunu tanımlayabilir ve bir kez ya da hiç değerlendirir, ancak aşağıdaki konularda her erişimine çifte kontrol kilitleme biraz yük oluşur tembel bir değerlendirme alırsınız lazy val
.
@SargeBorsch'un belirttiği gibi, yöntemi tanımlayabilirsiniz ve bu en hızlı seçenektir:
def even(i: Int): Boolean = i % 2 == 0
Ancak, işlev kompozisyonu veya daha yüksek düzeydeki işlevler (gibi) için bir işleve (yönteme değil) ihtiyacınız varsa, filter(even)
derleyici, işlev olarak her kullanışınızda yönteminizden bir işlev oluşturur, bu nedenle performans, bundan biraz daha kötü olabilir val
.
even
çağrıldığında işlevi değerlendirmek önemli değil mi ?
def
bir yöntemi tanımlamak için kullanılabilir ve bu en hızlı seçenektir. @ A.Karimi
even eq even
,.
@inline
nitelik bunun için. Ancak işlevler satır içi yapılamaz çünkü işlev çağrısı apply
bir işlev nesnesinin sanal yöntemine yapılan bir çağrıdır . JVM bu gibi çağrıları bazı durumlarda devredebilir ve sıralı olarak sıralayabilir, ancak genel olarak değil.
Bunu düşün:
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
Farkı görüyor musunuz? Kısacası:
def : Her çağrı için even
, even
yöntemin gövdesini tekrar çağırır . Ancak even2
ie val ile , işlev bildirim sırasında yalnızca bir kez başlatılır (ve dolayısıyla val
satır 4'e yazdırır ve bir daha asla yazdırılmaz ) ve her erişimde aynı çıktı kullanılır. Örneğin bunu yapmayı deneyin:
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
Zaman x
başlatılır, değer tarafından döndürülen Random.nextInt
nihai değeri olarak ayarlanır x
. Bir dahaki sefere x
tekrar kullanıldığında, her zaman aynı değeri döndürür.
Ayrıca tembel olarak başlatabilirsiniz x
. yani ilk kullanıldığında deklarasyon değil, başlatılır. Örneğin:
scala> lazy val y = { Random.nextInt }
y: Int = <lazy>
scala> y
res4: Int = 323930673
scala> y
res5: Int = 323930673
even2
Bir kez 1
ve bir kez olmak üzere iki kez aramayı deneyin 2
. Her aramada farklı cevaplar alacaksınız. Bu nedenle, println
sonraki aramalarda yürütülmezken , farklı aramalardan aynı sonucu alamazsınız even2
. Neden println
tekrar yürütülmediğine gelince , bu farklı bir soru.
Bunu gör:
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)
Şaşırtıcı bir şekilde, bu 9 değil 4 yazacaktır! val (çift var) derhal değerlendirilir ve atanır.
Şimdi val değerini def olarak değiştirin .. 9 yazdıracaktır! Def bir işlev çağrısıdır .. her çağrıldığında değerlendirir.
val yani "sq" Scala tanımı ile sabittir. Doğru beyan edildiği anda değerlendirilir, daha sonra değiştiremezsiniz. Başka örneklerde, even2 de val'dir, ancak işlev imzasıyla bildirilir yani "(Int => Boolean)", bu nedenle Int türü değildir. Bu bir işlevdir ve değeri aşağıdaki ifadeyle ayarlanır
{
println("val");
(x => x % 2 == 0)
}
Scala val özelliğine göre, sq ile aynı kuralı even2'ye başka bir işlev atayamazsınız.
Eval2 val işlevini neden tekrar tekrar "val" yazdırmıyor?
Orig kodu:
val even2: (Int => Boolean) = {
println("val");
(x => x % 2 == 0)
}
Biliyoruz ki, Scala'da yukarıdaki ifade türünün son ifadesi ({..} içinde) aslında sol tarafa dönüyor. Böylece even2 val türü için (Int => Boolean) için bildirdiğiniz türle eşleşen even2 - "x => x% 2 == 0" fonksiyonunu ayarlıyorsunuz, böylece derleyici mutlu oluyor. Şimdi even2 sadece "(x => x% 2 == 0)" fonksiyonunu gösteriyor (yani println ("val") öncesi başka bir ifade değil). Farklı parametrelerle event2 çağırmak aslında "(x => x% 2) == 0) "kodu, yalnızca event2 ile kaydedildiğinden.
scala> even2(2)
res7: Boolean = true
scala> even2(3)
res8: Boolean = false
Sadece daha fazla açıklamak için, kodun farklı versiyonu aşağıdadır.
scala> val even2: (Int => Boolean) = {
| println("val");
| (x => {
| println("inside final fn")
| x % 2 == 0
| })
| }
Ne olacak ? Burada, even2 () öğesini çağırdığınızda tekrar tekrar "iç final fn" nin basılı olduğunu görüyoruz.
scala> even2(3)
inside final fn
res9: Boolean = false
scala> even2(2)
inside final fn
res10: Boolean = true
scala>
Gibi bir tanımın yürütülmesi def x = e
ifadeyi değerlendirmeyecektir e. Bunun yerine x her çağrıldığında e değerlendirilir.
Alternatif olarak Scala, tanımın val x = e
değerlendirmesinin bir parçası olarak sağ tarafı değerlendiren bir değer tanımı sunar
. Daha sonra x kullanılırsa, hemen e'nin önceden hesaplanmış değeri ile değiştirilir, böylece ifadenin tekrar değerlendirilmesi gerekmez.
ayrıca, Val bir değer değerlendirmesidir. Yani sağ taraftaki ifade tanım sırasında değerlendirilir. Def ad değerlendirmesine göre. Kullanılıncaya kadar değerlendirme yapmaz.
Yukarıdaki faydalı yanıtlara ek olarak, bulgularım:
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
Yukarıda "def", çağrıldığında başka bir "Int => Int" işlevi döndüren bir yöntemdir (sıfır bağımsız değişken parametresi ile).
Yöntemlerin işlevlere dönüştürülmesi burada iyi açıklanmıştır: https://tpolecat.github.io/2014/06/09/methods-functions.html
REPL'de,
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
, talep üzerine değerlendirilir
val call-by-value
, başlatma sırasında değerlendirilen anlamına gelir
Int => Boolean
geliyor? Bencedef foo(bar: Baz): Bin = expr