Scala Fonksiyonlarını okudum ( Scala'nın başka bir turunun parçası ) Bu yazıda şunları söyledi:
Yöntemler ve işlevler aynı şey değil
Ama bu konuda hiçbir şey açıklamadı. Ne demeye çalışıyordu?
Scala Fonksiyonlarını okudum ( Scala'nın başka bir turunun parçası ) Bu yazıda şunları söyledi:
Yöntemler ve işlevler aynı şey değil
Ama bu konuda hiçbir şey açıklamadı. Ne demeye çalışıyordu?
Yanıtlar:
Jim blog yazısında bunu çok fazla ele aldı , ama burada referans için bir brifing gönderiyorum.
İlk olarak, Scala Spesifikasyonunun bize ne söylediğine bakalım. Bölüm 3 (türler) bize İşlev Türleri (3.2.9) ve Yöntem Türleri (3.3.1) hakkında bilgi verir. Bölüm 4'te (temel bildirimler) Değer Beyanı ve Tanımları (4.1), Değişken Beyanı ve Tanımları (4.2) ve Fonksiyon Beyanları ve Tanımları (4.6) anlatılmaktadır. Bölüm 6'da (ifadeler) Anonim İşlevler (6.23) ve Yöntem Değerleri (6.7) anlatılmaktadır. Merakla, fonksiyon değerleri 3.2.9'da bir kez konuşulur ve başka yerde yoktur.
Bir Fonksiyon Tip formunun (kabaca) tipidir (T1, ..., Tn) => u özelliği için bir kısaltmadır, FunctionN
standart kütüphanesinde. Anonim İşlevler ve Yöntem Değerler işlev türlerine sahiptir ve işlev türleri değer, değişken ve işlev bildirimleri ve tanımlarının bir parçası olarak kullanılabilir. Aslında, bir yöntem türünün bir parçası olabilir.
Bir Yöntem a, olmayan bir değer türü . Araçlarının orada hiçbir hiçbir nesne, herhangi bir örneği - - bir yöntem türü ile değer. Yukarıda belirtildiği gibi, bir Yöntem Değeri aslında bir İşlev Türüne sahiptir . Bir yöntem türü bir def
bildiridir - def
bedeni hariç her şey .
Değer Beyanları ve Tanımlar ve Değişken Bildirimleri ve Tanımlar vardır val
ve var
her ikisi de dahil olmak üzere beyanları, tipi ve değeri - hangi olabilir, sırasıyla Fonksiyon Tipi ve Anonim Fonksiyonlar veya Yöntem Değerler . JVM'de bunların (yöntem değerleri) Java'nın "yöntemler" dediği şeyle uygulandığını unutmayın.
Bir Fonksiyon Deklarasyonu bir olduğunu def
dahil beyannamesi, tip ve vücutta . Yazım kısmı, Yöntem Türü'dür ve gövde bir ifade veya bloktur . Bu, Java'nın "yöntemler" dediği JVM'de de uygulanır.
Son olarak, Anonim İşlev , İşlev Türü'nün (yani, özelliğin bir örneği FunctionN
) bir örneğidir ve Yöntem Değeri aynı şeydir! Fark, (a yöntem değer yöntemleri, ya bir alt postfixing oluşturulur ki m _
, (a yöntem değeri "fonksiyonu beyanı" tekabül etmektedir def
) m
), ya da adı verilen bir işlem ile eta-genişleme yöntemi otomatik bir döküm gibi, işlev.
Spesifikasyonlar böyle söylüyor, bu yüzden bunu öne koymama izin verin: bu terminolojiyi kullanmıyoruz! Çok fazla sözde arasındaki karışıklığa yol açar "işlevi beyanı" ve - programının bir parçası (temel beyanlar bölüm 4) 'dir, "anonim işlev" bir ifadesidir, ve "fonksiyonu tipi" olduğu, iyi bir tür - bir özellik.
Aşağıdaki terminoloji ve deneyimli Scala programcılar tarafından kullanılan, şartnamenin terminoloji itibaren bir değişiklik yapar: demek yerine işlevi bildirimi , söylediğimiz yöntem . Hatta yöntem beyanı bile. Ayrıca, değer bildirimlerinin ve değişken bildirimlerin de pratik amaçlı yöntemler olduğunu belirtiyoruz .
Yani, terminolojideki yukarıdaki değişiklik göz önüne alındığında, işte bu ayrımın pratik bir açıklaması.
Bir fonksiyon biri içeren bir amacı, FunctionX
örneğin, özellikleri, Function0
, Function1
, Function2
, vs. de dahil olmak üzere olabilir PartialFunction
aslında uzanan yanı Function1
.
Bu özelliklerden biri için tür imzasını görelim:
trait Function2[-T1, -T2, +R] extends AnyRef
Bu özelliğin bir soyut yöntemi vardır (birkaç somut yöntemi de vardır):
def apply(v1: T1, v2: T2): R
Ve bu bize bunun hakkında bilinmesi gereken her şeyi anlatıyor. Bir fonksiyonun , T1 , T2 , ..., TN tipindeki N parametrelerini alan ve türünde bir şey döndüren bir apply
yöntemi vardır . Aldığı parametrelerde kontra-varyant ve sonuçta ko-varyanttır.R
Bu varyans Function1[Seq[T], String]
a'nın bir alt türü olduğu anlamına gelir Function1[List[T], AnyRef]
. Bir alt tip olması , onun yerine kullanılabileceği anlamına gelir . Birisi arayacak f(List(1, 2, 3))
ve bir AnyRef
geri bekleyecek olursam , yukarıdaki iki türden birinin işe yarayacağını kolayca görebilirsiniz .
Şimdi, bir yöntemin ve fonksiyonun benzerliği nedir? Peki, eğer f
bir işlev ve m
kapsam için yerel bir yöntemse, her ikisi de şu şekilde çağrılabilir:
val o1 = f(List(1, 2, 3))
val o2 = m(List(1, 2, 3))
Bu çağrılar aslında farklıdır, çünkü ilki sadece sözdizimsel bir şekerdir. Scala bunu genişletir:
val o1 = f.apply(List(1, 2, 3))
Tabii ki, nesne üzerinde bir yöntem çağrısıdır f
. Fonksiyonların avantajı için başka sözdizimsel şekerleri de vardır: fonksiyon değişmezleri (aslında ikisi) ve (T1, T2) => R
tip imzaları. Örneğin:
val f = (l: List[Int]) => l mkString ""
val g: (AnyVal) => String = {
case i: Int => "Int"
case d: Double => "Double"
case o => "Other"
}
Bir yöntem ve bir fonksiyon arasındaki diğer bir benzerlik, birincisinin kolayca ikincisine dönüştürülebilmesidir:
val f = m _
Scala genişleyecektir olduğunu varsayarak m
türü olan (List[Int])AnyRef
(Scala 2.7) içine:
val f = new AnyRef with Function1[List[Int], AnyRef] {
def apply(x$1: List[Int]) = this.m(x$1)
}
Scala 2.8'de, aslında AbstractFunction1
sınıf boyutlarını azaltmak için bir sınıf kullanır .
Bir işlevden bir yönteme - tersi dönüştürülemez dikkat edin.
Bununla birlikte, yöntemlerin büyük bir avantajı vardır (iki, biraz daha hızlı olabilirler): tip parametrelerini alabilirler . Örneğin, f
yukarıdakiler zorunlu olarak alacağı türü belirtebilir List
( List[Int]
örnekte), m
parametreleştirebilir:
def m[T](l: List[T]): String = l mkString ""
Bunun hemen hemen her şeyi kapsadığını düşünüyorum, ancak bunu kalan soruların cevaplarıyla tamamlamaktan mutluluk duyacağım.
val f = m
olarak derleyici tarafından val f = new AnyRef with Function1[List[Int], AnyRef] { def apply(x$1: List[Int]) = this.m(x$1) }
size işaret olmalıdır this
içindeki apply
yöntemle ifade etmez AnyRef
nesne, ancak kimin yöntemi nesneye val f = m _
değerlendirilir ( dış this
, tabiri caizse ), çünkü this
kapak tarafından yakalanan değerler arasındadır (örneğin return
aşağıda belirtildiği gibi).
Bir yöntem ve fonksiyon arasındaki büyük pratik fark, ne return
anlama geldiğidir. return
sadece bir yöntemden döner. Örneğin:
scala> val f = () => { return "test" }
<console>:4: error: return outside method definition
val f = () => { return "test" }
^
Bir yöntemde tanımlanan bir işlevden dönmek, yerel olmayan bir dönüş yapar:
scala> def f: String = {
| val g = () => { return "test" }
| g()
| "not this"
| }
f: String
scala> f
res4: String = test
Oysa yerel bir yöntemden dönmek yalnızca bu yöntemden döner.
scala> def f2: String = {
| def g(): String = { return "test" }
| g()
| "is this"
| }
f2: String
scala> f2
res5: String = is this
for (a <- List(1, 2, 3)) { return ... }
? Bu, kapanmaya kadar şekersizleşir.
return
fonksiyonu bir değer iade ve bazı formu escape
veya break
veya continue
yöntemleri dönün.
işlev Sonuç üretmek için bir işlev listesi içeren bir işlev çağrılabilir. Bir işlevin bir parametre listesi, bir gövde ve bir sonuç türü vardır. Sınıf, özellik veya singleton nesnesinin üyesi olan işlevlere yöntem denir . Diğer işlevler içinde tanımlanan işlevlere yerel işlevler denir. Birimin sonuç türüne sahip işlevlere yordamlar denir. Kaynak koddaki anonim işlevlere işlev değişmezleri denir. Çalışma zamanında işlev değişmezleri işlev değerleri adı verilen nesnelere örneklenir.
Scala Second Edition'da Programlama. Martin Odersky - Lex Kaşık - Bill Venners
Diyelim bir listeniz var
scala> val x =List.range(10,20)
x: List[Int] = List(10, 11, 12, 13, 14, 15, 16, 17, 18, 19)
Bir Yöntem Tanımlama
scala> def m1(i:Int)=i+2
m1: (i: Int)Int
Bir İşlev Tanımlama
scala> (i:Int)=>i+2
res0: Int => Int = <function1>
scala> x.map((x)=>x+2)
res2: List[Int] = List(12, 13, 14, 15, 16, 17, 18, 19, 20, 21)
Tartışmayı Kabul Eden Yöntem
scala> m1(2)
res3: Int = 4
Val ile Fonksiyon Tanımlama
scala> val p =(i:Int)=>i+2
p: Int => Int = <function1>
İşlev bağımsız değişkeni isteğe bağlıdır
scala> p(2)
res4: Int = 4
scala> p
res5: Int => Int = <function1>
Metot Argümanı Zorunludur
scala> m1
<console>:9: error: missing arguments for method m1;
follow this method with `_' if you want to treat it as a partially applied function
Yöntem Vs İşlevi ile farklı diff örneği gibi diğer örneklerle diğer farklılıkları aktarmayı açıklayan aşağıdaki Öğreticiyi denetleyin, işlevi Değişkenler olarak kullanma, işlevi döndüren işlev oluşturma
Burada, açıklamalarımın çoğunun alındığı güzel bir makale var . Benim anlayışımla ilgili İşlevler ve Yöntemlerin kısa bir karşılaştırması. Umarım yardımcı olur:
Fonksiyonlar : Temelde bir nesnedir. Daha kesin olarak, fonksiyonlar bir uygulama yöntemine sahip nesnelerdir; Bu nedenle, yükleri nedeniyle yöntemlerden biraz daha yavaştırlar. Çağırılacak bir nesneden bağımsız olmaları bakımından statik yöntemlere benzer. Bir fonksiyonun basit bir örneği tıpkı aşağıdaki gibidir:
val f1 = (x: Int) => x + x
f1(2) // 4
Yukarıdaki satır, bir nesneyi object1 = object2 gibi diğerine atamaktan başka bir şey değildir. Aslında örneğimizdeki object2 anonim bir işlevdir ve sol taraf bu nedenle bir nesnenin türünü alır. Bu nedenle, şimdi f1 bir nesnedir (İşlev). Anonim işlev aslında Int türünde 1 parametre ve Int türünde dönüş değeri olan bir işlev anlamına gelen Function1 [Int, Int] işlevinin bir örneğidir. Bağımsız değişkenler olmadan f1 çağrısı bize anonim işlevin imzasını verecektir (Int => Int =)
Yöntemler : Nesneler değildir, bir sınıf örneğine, yani bir nesneye atanırlar. Java'daki yöntemle veya c ++ 'daki üye işlevlerle tam olarak aynı ( Raffi Khatchadourian'ın bu soruya yaptığı bir yorumda işaret ettiği gibi ) vb. Bir yöntemin basit bir örneği, aşağıdaki gibidir:
def m1(x: Int) = x + x
m1(2) // 4
Yukarıdaki satır basit bir değer ataması değil, bir yöntemin tanımıdır. Bu yöntemi ikinci satır gibi 2 değeriyle çağırdığınızda, x 2 ile değiştirilir ve sonuç hesaplanır ve çıktı olarak 4 elde edersiniz. Burada sadece yöntem olduğu ve giriş değerine ihtiyaç duyduğu için sadece m1 yazıyorsanız bir hata alırsınız. _ Kullanarak aşağıdaki gibi bir işleve bir yöntem atayabilirsiniz:
val f2 = m1 _ // Int => Int = <function1>
İşte Rob Norris'in farkı anlatan harika bir yazı , işte TL; DR
Scala'daki yöntemler değer değil, işlevlerdir. Bir metoda delege eden bir işlevi η-expanding (arkadaki alt çizgi nesnesi tarafından tetiklenen) aracılığıyla oluşturabilirsiniz.
aşağıdaki tanım ile:
bir yöntem def ile tanımlanan bir değerdir ve bir değer val'e atayabileceğiniz bir şeydir
Özetle ( blogdan alıntı ):
Bir yöntem tanımladığımızda, a yöntemine atayamayacağımızı görüyoruz val
.
scala> def add1(n: Int): Int = n + 1
add1: (n: Int)Int
scala> val f = add1
<console>:8: error: missing arguments for method add1;
follow this method with `_' if you want to treat it as a partially applied function
val f = add1
Ayrıca Not türünü ait add1
olağan görünmüyor; bir değişken türü bildiremezsiniz(n: Int)Int
. Yöntemler değer değildir.
Ancak, η-genişletme postfix operatörünü ekleyerek (η “eta” olarak telaffuz edilir), yöntemi bir fonksiyon değerine dönüştürebiliriz. Türüne dikkat edin f
.
scala> val f = add1 _
f: Int => Int = <function1>
scala> f(3)
res0: Int = 4
Etkisi _
aşağıdakilerin eşdeğerini gerçekleştirmektir: Function1
yöntemimize yetki veren bir örnek oluştururuz.
scala> val g = new Function1[Int, Int] { def apply(n: Int): Int = add1(n) }
g: Int => Int = <function1>
scala> g(3)
res18: Int = 4
Scala 2.13'te, işlevlerin aksine, yöntemler alabilir / döndürebilir
Bununla birlikte, bu kısıtlamalar Polimorfik fonksiyon türleri # 4672 tarafından dotty (Scala 3) olarak kaldırılır , örneğin dotty sürüm 0.23.0-RC1 aşağıdaki sözdizimini etkinleştirir
Tür parametreleri
def fmet[T](x: List[T]) = x.map(e => (e, e))
val ffun = [T] => (x: List[T]) => x.map(e => (e, e))
Örtük parametreler ( bağlam parametreleri)
def gmet[T](implicit num: Numeric[T]): T = num.zero
val gfun: [T] => Numeric[T] ?=> T = [T] => (using num: Numeric[T]) => num.zero
Bağımlı tipler
class A { class B }
def hmet(a: A): a.B = new a.B
val hfun: (a: A) => a.B = hmet
Daha fazla örnek için bkz. Test / run / polimorfik-fonksiyonlar.scala
Pratik olarak, bir Scala programcısı sadece fonksiyonları ve yöntemleri düzgün kullanmak için aşağıdaki üç kuralı bilmelidir:
def
ve tarafından tanımlanan işlev değişmezleri=>
işlevlerdir. Scala Programlama kitabının 4. baskısında, sayfa 143, Bölüm 8'de tanımlanmıştır.someNumber.foreach(println)
Scala'da Programlamanın dört basımından sonra, insanlar için iki önemli kavramı ayırt etmek hala bir sorundur: fonksiyon ve fonksiyon değeri, çünkü tüm basımlar açık bir açıklama vermemektedir. Dil belirtimi çok karmaşık. Yukarıdaki kuralların basit ve doğru olduğunu gördüm.