İçeriğe geç
academia.sh

Ders 13 / 13

Ne Zaman Genellik

Aynı toplam alan hesabı üç yazımla kuruluyor: boş arayüzle üç tip iddiası, genellikle sıfır ama T tek bir somut tipe sabitlenmiş, arayüz sözleşmesiyle yine sıfır ama farklı somut tipler aynı dilimde durabiliyor. Sınırlayıcı ölçüm, T bir arayüze sabitlendiğinde (heterojen bir koleksiyonu kabul etmek için) genelliğin kazancının kaybolduğunu gösteriyor: eleman boyutu 8 bayttan 16 bayta çıkıyor, tıpkı sıradan bir arayüz değeri gibi. Kurs on altı bağın 4'ünü bildirimde, 4'ünü kullanım yerinde, 8'ini değerin içinde buldu.

İçindekiler

Genel Veri Yapıları dersi genelliğin üç ders boyunca hep kaynağın içinde kaldığını gösterdi: tip parametresi ya çıkarıldı ya bildirimde açıkça yazıldı, ama hiçbir zaman değere girmedi. Arayüz konusu tam tersini ölçmüştü — dinamik tip değerin içinde taşınıyor ve ancak değere sorularak okunabiliyordu. Bu kapanış dersi iki yazımı, ve aralarında duran arayüz sözleşmesini, aynı işi üç kez yaparak yan yana koyar: bir toplam alan hesabı boş arayüzle, genellikle ve arayüz sözleşmesiyle kurulur, ve üçünde de bağın nerede kurulduğu sayılır.

Kursun üçüncü iddiası buydu: genellik ile arayüz aynı işi iki ayrı yerde yapıyor. Genellikte bağ bildirimde ve kullanım yerinde kurulur, değere hiç girmez; arayüzde bağ değere girer. Bu ders bu farkı ilk kez doğrudan koşturarak, aynı hesaplamayı üç kez yazarak, ölçer.

Önceki üç ders genelliği hep kendi başına ölçtü — arayüzle yan yana koymadan. Bu, kursun genel çerçevesine bir borç bıraktı: “genellikte bağ değere girmiyor” iddiası, bir karşılaştırma yapılmadan yalnız bir gözlemdi. Bu ders o borcu kapatıyor: aynı iş hem genellikle hem arayüzle yazılıp aynı koşumda ölçülüyor, ve fark bir iddia olmaktan çıkıp bir sayıya (8 bayta karşı 16 bayt) dönüşüyor.

Aynı İş, Üç Yazım

Ölçülen iş basit: bir koleksiyondaki şekillerin toplam alanını hesaplamak. Kare ve Daire kendi Alan() yöntemini taşıyor; Sekil arayüzü bu tek yöntemi listeliyor. Bu iş bilinçli seçildi: alan hesabı tipe göre gerçekten değişiyor — bir karenin alanı kenarın karesi, bir dairenin alanı yarıçapın karesiyle bir sabitin çarpımı — ama toplama işleminin kendisi her tip için aynı. Üç yazımın da başarması gereken iş bu yüzden iki parçaya ayrılıyor: tipe özgü kısım (Alan()in kendi gövdesi) ve tipten bağımsız kısım (toplamı biriktirmek).

// uc.go — ayni is uc yazimla: bos arayuz, genellik, arayuz sozlesmesi
package main

import (
	"fmt"
	"unsafe"
)

type Kare struct{ Kenar float64 }

func (k Kare) Alan() float64 { return k.Kenar * k.Kenar }

type Daire struct{ YariCap float64 }

func (d Daire) Alan() float64 { return 3.14159 * d.YariCap * d.YariCap }

type Sekil interface{ Alan() float64 }

// 1) bos arayuzle: her eleman icin bir tip iddiasi (tip anahtari dali) gerekiyor.
func toplamAlanAny(d []any) (toplam float64, iddiaSayisi int) {
	for _, e := range d {
		switch v := e.(type) {
		case Kare:
			iddiaSayisi++
			toplam += v.Alan()
		case Daire:
			iddiaSayisi++
			toplam += v.Alan()
		}
	}
	return
}

// 2) genellikle: govde her T icin ayni, T homojen kaldigi surece hic tip iddiasi yok.
func toplamAlanGenel[T Sekil](d []T) (toplam float64) {
	for _, e := range d {
		toplam += e.Alan()
	}
	return
}

// 3) arayuz sozlesmesiyle: farkli somut tipler ayni dilimde, hicbir tip iddiasi yok.
func toplamAlanArayuz(d []Sekil) (toplam float64) {
	for _, e := range d {
		toplam += e.Alan()
	}
	return
}

func main() {
	fmt.Println("-- ayni is, uc yazim --")

	karisik := []any{Kare{Kenar: 2}, Daire{YariCap: 1}, Kare{Kenar: 3}}
	tAny, iddia := toplamAlanAny(karisik)
	fmt.Printf("bos arayuz    toplam=%.4f  tip iddiasi=%d  bildirim satiri=0\n", tAny, iddia)

	homojen := []Kare{{Kenar: 2}, {Kenar: 3}}
	tGenel := toplamAlanGenel(homojen)
	fmt.Printf("genellik      toplam=%.4f  tip iddiasi=0  bildirim satiri=1 (kisit)\n", tGenel)

	sozlesmeli := []Sekil{Kare{Kenar: 2}, Daire{YariCap: 1}, Kare{Kenar: 3}}
	tArayuz := toplamAlanArayuz(sozlesmeli)
	fmt.Printf("arayuz        toplam=%.4f  tip iddiasi=0  bildirim satiri=1 (arayuz)\n", tArayuz)

	fmt.Println()
	fmt.Println("-- sinirlayici olcum: T bir arayuze sabitlenince kazanc kayboluyor --")
	heterojen := []Sekil{Kare{Kenar: 2}, Daire{YariCap: 1}}
	tDejenere := toplamAlanGenel(heterojen)
	fmt.Printf("toplamAlanGenel[Kare](homojen)    T=Kare   eleman boyutu=%d bayt  sonuc=%.4f\n",
		unsafe.Sizeof(homojen[0]), tGenel)
	fmt.Printf("toplamAlanGenel[Sekil](heterojen) T=Sekil  eleman boyutu=%d bayt  sonuc=%.4f\n",
		unsafe.Sizeof(heterojen[0]), tDejenere)
}
-- ayni is, uc yazim --
bos arayuz    toplam=16.1416  tip iddiasi=3  bildirim satiri=0
genellik      toplam=13.0000  tip iddiasi=0  bildirim satiri=1 (kisit)
arayuz        toplam=16.1416  tip iddiasi=0  bildirim satiri=1 (arayuz)

-- sinirlayici olcum: T bir arayuze sabitlenince kazanc kayboluyor --
toplamAlanGenel[Kare](homojen)    T=Kare   eleman boyutu=8 bayt  sonuc=13.0000
toplamAlanGenel[Sekil](heterojen) T=Sekil  eleman boyutu=16 bayt  sonuc=7.1416

GN13. İlk üç satır aynı hesaplamanın üç kaynağını karşılaştırıyor. Boş arayüz yazımı üç elemanın üçü için de bir tip iddiası (tip anahtarının bir dalı) çalıştırıyor ve hiçbir arayüz bildirmiyor — sözleşme kaynakta hiçbir yerde yazılı değil, yalnız switch içindeki case dalları örtük olarak hangi tiplerin bekleneceğini söylüyor. Genellik yazımı sıfır tip iddiasıyla çalışıyor ama homojen yalnız Kare içeriyor — T, Genel Veri Yapıları dersinin ölçtüğü gibi, çağıranın bildiriminde tek bir somut tipe sabitleniyor. Arayüz yazımı da sıfır tip iddiasıyla çalışıyor ve üstelik Kare ile Daireyi aynı dilimde taşıyabiliyor — sozlesmeli ikisini birden içeriyor, homojen içeremezdi.

Sınırlayıcı Ölçüm: T Bir Arayüze Sabitlenince Kazanç Kayboluyor

GN14. Son iki satır genelliğin sınırını gösteriyor. toplamAlanGenel, kısıtı bir yöntem listesi olduğu için, Tyi bir arayüz tipiyle de örnekleyebiliyor — toplamAlanGenel[Sekil](heterojen) derleniyor ve Kare ile Daireyi karışık bir dilimde kabul ediyor. Ama bunun bedeli ölçülüyor: T=Kare iken her eleman 8 bayt (yalnız Kenar alanının kendisi), T=Sekil iken her eleman 16 bayt — arayüz çiftinin taşıdığı tip işaretçisi ile veri işaretçisinin toplamı. Tyi bir arayüze sabitlemek, genelliğin sıfır maliyetli sözünü bozuyor: kod artık toplamAlanArayuzdan hiçbir farkı olmayan bir şey çalıştırıyor, yalnız bunu bir tip parametresinin arkasına gizlemiş durumda.

toplamAlanGenelin gövdesi iki çağrıda da harfi harfine aynı kaldı — for _, e := range d { toplam += e.Alan() } satırı ne T=Kare ne T=Sekil için değişti. Değişen yalnız Tnin ne olduğu, ve bu değişim eleman boyutunu iki katına çıkardı. Bu, genelliğin tanımının kendisinden geliyor: bir genel gövde, tek bir kaynaktan, örneklendiği her T için aynı işlemi çalıştırır. T=Kare iken bu işlem Kare.Alan()i doğrudan, ek bir katman olmadan çağırıyor; T=Sekil iken aynı işlem artık bir arayüz değeri üzerinden dolaylı bir yöntem çağrısı yapıyor — kaynakta hiçbir satır değişmese de, çalışma zamanında olan biten değişiyor.

Bu, kursun sorusuna kesin bir yanıt veriyor: genellik her zaman kazanmaz. Bir koleksiyonun bütün elemanları aynı somut tipteyse ve üzerlerinde yapılan işlem her tip için aynıysa, genellik hem sıfır tip iddiası hem sıfır ek bayt sağlıyor. Elemanlar farklı somut tipler taşımak zorundaysa — davranış tipe göre gerçekten değişmesi gerekiyorsa — iki seçenek kalıyor: Tyi bir arayüze sabitleyip genelliğin kazancından vazgeçmek, ya da baştan arayüz yazmak. İkincisi daha dürüst bir yazım, çünkü kaynak zaten neyin olacağını — birden fazla somut tipin aynı yerde duracağını — söylüyor. Genel bir gövde, tanımı gereği, örneklendiği her T için aynı kalıyor; farklı Tler için farklı davranmasını istemek, genelliğin kendisiyle çelişiyor.

Kurs Kapanışı

Arayüzler, Hatalar ve Genellikler kursu tek bir soruyla açıldı: Go’da bir tipin bir arayüzü karşıladığı hiçbir yerde yazmıyor — öyleyse bu bağ nerede kuruluyor? On üç ders bu soruyu üç düzenekte sordu: arayüzlerde (örtük gerçekleştirim, boş arayüz, tip iddiası, gömme, tüketici tarafında tanımlama), hata değerlerinde (hata arayüzü, nöbetçi hatalar, sarmalama, panik) ve genelliklerde (tip parametresi, tip kısıtı, genel veri yapıları, ve bu derste üçünün karşılaştırılması).

Birinci iddia doğrulandı: on altı bağın sekizi değerin içinde taşınıyor, çünkü Go’nun arayüz değeri bir çift — hem somut tip hem veri. İkinci iddia doğrulandı: dört bağ kullanım yerinde kuruluyor ve kaynak, bir tipin bir arayüzü karşıladığını hiçbir yerde söylemiyor. Üçüncü iddia bu derste doğrudan koşturuldu: genellik ile arayüz aynı işi iki ayrı yerde yapıyor — genellikte bağ bildirimde ve kullanım yerinde kalıyor, arayüzde değere giriyor — ve genelliğin bu kazancı, tip parametresi bir arayüze sabitlendiği anda kayboluyor.

Bu üç düzenekten biri, “kullanım yeri”, bir uyarı taşıyor: kullanım yeri derleyicinin baktığı yerdir, kaynağın işaret ettiği yer değildir. Genel Fonksiyonlar dersi bunu doğrudan derleyiciye sorarak — kaynağı geçici bir pakete yazıp yalnız geçip geçmediğini okuyarak — kanıtladı; Genel Veri Yapıları ve Tip Kısıtları dersleri de aynı yöntemi kullandı. Dolaylı bir kanıt (yalnız kaynakta bir sözleşmenin yazılı olmadığını göstermek) ile doğrudan bir kanıt (derleyicinin nerede durduğunu koşturarak görmek) arasındaki bu fark, kursun kendi ölçüm disiplininin bir parçası: bir ders “kullanım yerinde kuruluyor” diyorsa, ya derleyiciyi çağırıp nerede durduğunu göstermeli, ya da kanıtın dolaylı olduğunu açıkça yazmalıdır.

Kurs aşırı bir genelleme yapmadan kapanıyor: bu üç düzenek (bildirim, kullanım yeri, değer) Go’nun bütün tip sistemini kapsamıyor, yalnız on üç dersin sorduğu tek soruya — bu bağ nerede kuruluyor? — verilen yanıtları sınıflıyor. Aşağıdaki tablo bu on üç yanıtı tek satırda topluyor; bir satır boş bırakıldı, çünkü karşılık geldiği ders bu kapanış yazıldığı sırada henüz diskte değildi — hücre uydurulmadı.

Ders Ölçülen bağ Kurulduğu yer (bildirim · kullanım yeri · değer) Sınırlayıcı ölçüm
Arayüz Kavramı Bir tipin bir arayüzü örtük karşılaması; küçük arayüz ilkesi kullanım yeri (4/4/8 dağılımı burada üretildi) Derleme zamanı uyum kanıtı bağı bildirime taşır, kanıt yokken derleyici sessiz kalır
Boş Arayüz ve any Boş arayüzün tip bilgisini değere taşıması değer nil işaretçili arayüz nil değildir
Tip İddiası ve Tip Anahtarı Değere sorma: iki yanıtlı iddia, tip anahtarının seçtiği dal değer Aynı değer hem somut hem arayüz dalına uyabilir, seçilen dal yazılış sırasına bağlı
Arayüz Gömme Sözleşme bileşimi bildirim Gömme yalnız sözleşmeyi birleştirir, gerçekleştirimi birleştirmez
Arayüz Tasarımı Aynı somut tip, tüketicilerin tanımladığı dört bağımsız arayüzü karşılıyor; kaynağında sıfır atıf var kullanım yeri Aynı imzalı iki ayrı adlı arayüz örtük eşdeğer olur; eşdeğerlik ancak imza ayrılınca kırılır
Hata Arayüzü Hatanın örtük karşılanan bir değer olması kullanım yeri + değer nil olmayan arayüz nil somut değer taşıyabilir
Nöbetçi Hatalar Hata kimliğinin değerde durması değer Kimlik değişince çağıran sessizce yanlış dala düşer, uyarı üretilmez
Hata Sarmalama ve Çözme Zincirin değerde taşınması değer %v kullanmak zinciri koparır, metin aynı kalır
Panik ve Kurtarma Paniğin imzada hiçbir iz bırakmaması (bağ kurulmuyor) Kurtarma paniği yok etmez, taşır; kurtarılan değer yazılmazsa bilgi tümüyle kaybolur
Genel Fonksiyonlar Tip parametresinin çağrı yerinde çıkarılması kullanım yeri Kısıtsız T ile == ve + yazılamaz
Tip Kısıtları Kısıtın tip kümesi listelemesi bildirim Tip kümesi terimli arayüz yalnız kısıt konumunda geçerli
Genel Veri Yapıları Örneklemenin çağıranın bildiriminde durması bildirim Karşılaştırılabilirlik kapsayıcının alan yapısına da bağlı
Ne Zaman Genellik Aynı işin üç yazımda nerede bağlandığı (üçünün karşılaştırması) T bir arayüze sabitlenince genelliğin kazancı kayboluyor

Kurs boyunca ölçülen üç düzenek, sonuçta, tek bir alışkanlığa iniyor: bir kaynağı okurken “bu bağın nerede kurulduğu” sorusunu sormak. Bir bildirim satırı arandığında bulunuyorsa bağ oradadır; bulunmuyorsa iki yer kalıyor — ya derleyicinin kullanım yerinde kurduğu bir bağ (ki bunu görmenin tek yolu derleyiciyi çağırmaktır), ya da yalnız çalışan değere sorularak öğrenilebilecek bir bağ. Bu üç soruyu sırayla sormak, Go’nun tip sistemini okurken kaynağın söylediğiyle söylemediğini ayırmanın ölçülebilir bir yolu.

Özet

  • Aynı toplam alan hesabı üç yazımla kuruldu: boş arayüz üç tip iddiası gerektirdi, genellik ve arayüz sıfır tip iddiasıyla çalıştı.
  • Genellik yalnız T tek bir somut tipe sabitlendiğinde sıfır maliyetli kalıyor; T bir arayüze sabitlenince eleman boyutu 8 bayttan 16 bayta çıkıyor ve kazanç kayboluyor.
  • Davranış tipe göre gerçekten değişmesi gerekiyorsa arayüz gerekiyor, çünkü genel bir gövde örneklendiği her T için aynı kalıyor.
  • Kurs boyunca ölçülen on altı bağın dördü bildirimde, dördü kullanım yerinde, sekizi değerin içinde kuruldu; bu dağılım Go’nun tip sistemini değil, yalnız bu on üç dersin sorduğu soruyu sınıflıyor.
  • Genellik ile arayüz aynı işi iki ayrı yerde yapıyor: genellikte bağ değere hiç girmiyor, arayüzde giriyor — ve bu fark, ne zaman hangisinin seçileceğinin ölçülebilir karşılığı.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat