Ders 10 / 14
Biçimlendirici ve Tüy Toplayıcılar
Aynı işlevin altı ayrı yazımı altı ayrı kaynak metni ama tek bir ayrıştırma ağacı verir; biçimlendirici altı metni 1 ayrı sonuca indirir, tüy toplayıcının bulgu kümesini 5'ten 1'e düşürür ama kalan 6 bulguyu ve kâhinden sapan tek çağrıyı yerinde bırakır.
İçindekiler
Önceki ders kapsamı ölçtü ve kapsamın neyi saymadığını gösterdi: sekiz satırın sekizi yürüdü, üç testin üçü geçti, buna karşın dört girdi bileşiminden birinde gerçek davranış kâhinden ayrıldı. Kapsam kodun nerede yürüdüğünü sayar.
Bu ders bir adım yana gider: peki kodun nasıl yazıldığını kim tekleştirir? Aynı işlev bir ekipte kaç ayrı biçimde yazılabilir, ve bu sayı bir araca devredildiğinde kaça iner? Sorunun ölçülebilir olması için “biçim” tanımının kesin olması gerekir; bu ders o tanımı kurup sayıyı okur.
İki Ayrı Araç Sınıfı
İki araç sınıfı sık sık aynı cümlede anılır ve karıştırılır.
Biçimlendirici (formatter) kaynağı yeniden yazar. Girdisi bir kaynak metni, çıktısı yine bir kaynak metnidir; ikisinin ayrıştırma ağacı aynıdır, düzeni ayrıdır. Biçimlendiricinin ürettiği sonuç tektir: aynı ağaçtan hangi metinle girilirse girilsin aynı metin çıkar.
Tüy toplayıcı (linter) kaynağı bildirir. Girdisi bir kaynak metni, çıktısı bir bulgu listesidir; kaynağa dokunmaz. Bulguların bir bölümü düzenle ilgilidir, bir bölümü değildir — ve bu ayrım dersin ölçtüğü şeyin merkezinde durur.
İki sınıfın çıktısı da ayrı türdendir ve bu, aralarındaki en somut farktır. Biçimlendiricinin çıktısı kaynağın yerine geçer; tüy toplayıcının çıktısı kaynağın yanında durur. Birincisi bir dönüşümdür ve tersi alınamaz, çünkü silinen düzen bilgisi geri getirilemez. İkincisi bir gözlemdir ve kaynağı olduğu gibi bırakır; bulguyu okuyup ne yapılacağına karar veren yine yazan kişidir.
Biçim tartışmasının bir araca devredilmesi Kod İnceleme ve Ekip Süreci kursunda, inceleme ekseninin bir maddesi olarak ölçülmüştü; orada sorulan soru, incelemenin kaç yorumunun biçim üzerine olduğuydu. Burada soru başkadır ve tartışmanın kendisiyle ilgilenmez: aynı kaynağın kaç ayrı yazımı var, ve bu sayı kaça iniyor?
Modelin Kurulumu
Bu kursun hiçbir dersi araç adı yazmaz; araçların davranışı ders içinde modellenir ve modellendiği açıkça söylenir. Buradaki model üç parçadan oluşur.
Yazımlar. Ortak kurgunun üç kararlı indirim işlevi tek bir kanonik metinle yazılır.
Beş yazım daha, bu metne üç düzen dönüşümü uygulanarak üretilir: girinti adımını
değiştirmek, if gövdesini aynı satıra almak, işleç çevresindeki boşlukları silmek. Hiçbir
dönüşüm işlevin davranışına dokunmaz.
Biçimlendirici. Model, kaynağı ayrıştırıp ağaçtan yeniden yazan standart kitaplık işlevidir. Gerçek bir biçimlendirici yorumları korur, satır genişliği kuralı uygular ve uzun ifadeleri böler; bu model bunların hiçbirini yapmaz. Modelin tekleştirdiği şey yalnız düzendir, ve ölçüm de yalnız düzeni sorar.
Tüy toplayıcı. İki kural ailesi vardır. Metne bakan kurallar kaynağın satırlarını tarar: girinti adımı dört boşluk mu, bir satırda iki deyim var mı, işlecin iki yanında boşluk var mı. Ağaca bakan kurallar ayrıştırma ağacında dolaşır: adlandırılmamış sayısal sabit var mı, işlevin karar sayısı eşiği aşıyor mu. İkinci aile kaynağın nasıl yazıldığını hiç görmez, çünkü ağaç düzeni taşımaz.
Ölçümün varsayımları:
- KY1 — Altı yazım tek kanonik metinden üretilir; aralarındaki fark yalnızca girinti adımı, gövdenin satıra yerleşimi ve işleç çevresindeki boşluktur.
- KY2 — Yazımların özdeşliği iki ayrı yolla sınanır: ayrıştırma ağacının dökümü ve dört girdi bileşimindeki dönüş değerleri. İkisi de tabloda ayrı sütun olarak durur.
- KY3 — Biçimlendirici modeli yorum ve boş satır tutmaz; ölçüm bu yüzden yorumsuz bir kaynak üzerinde yapılır ve modelin sınırı burada bildirilir.
- KY4 — Metne bakan üç kural ile ağaca bakan iki kural, tüy toplayıcının tamamıdır. Bunlar bir kural kataloğu değil, iki aileyi ayırmaya yeten en küçük kümedir.
- KY5 — Bulgu kümesi sıralı bir demet olarak karşılaştırılır; iki yazımın bulgu kümesi aynıysa bir ayrı sonuç sayılır, metinleri ayrı olsa bile.
- KY6 —
indirimişlevinin gövdesi ortak tanımdaki gibidir ve değiştirilmez; kusurlu tavan yerinde durur. Kâhin ayrı yazılmıştır ve tavanı 25 olarak uygular. - KY7 — Hiçbir süre ölçülmez, hiçbir dosya yazılmaz. Sayılan şey ayrı metin, ayrı ağaç, ayrı davranış ve ayrı bulgu kümesidir.
Ölçüm
"""Bicimin tek sonuca indirgenmesi: alti yazim, kac ayri sonuc.""" import ast import re KANONIK = ( "def indirim(tutar, uye, kupon):\n" ' """Uc kararli bir islev."""\n' " oran = 0\n" " if uye:\n" " oran += 10\n" " if kupon:\n" " oran += 15\n" " if oran > 20:\n" " oran = 20\n" " return tutar - tutar * oran // 100\n" ) ISLECLER = ("+=", "//", ">", "-", "*") def girintile(k, adim): return "\n".join(" " * (adim * ((len(s) - len(s.lstrip())) // 4)) + s.lstrip() for s in k.split("\n")) def tek_satira(k): return re.sub(r"(?m)^(\s*)(if .+:)\n\s+(.+)$", r"\1\2 \3", k) def bosluksuzlastir(k): for op in ISLECLER: k = k.replace(f" {op} ", op) return k YAZIMLAR = { "dört boşluk": KANONIK, "iki boşluk": girintile(KANONIK, 2), "sekiz boşluk": girintile(KANONIK, 8), "tek satır gövde": tek_satira(KANONIK), "boşluksuz işleç": bosluksuzlastir(KANONIK), "ikisi birden": bosluksuzlastir(tek_satira(girintile(KANONIK, 2))), } def duzen_bulgulari(kaynak): """Metne bakan kurallar; bicimlendirici bunlari susturur.""" bulgu = [] girinti = [len(s) - len(s.lstrip()) for s in kaynak.split("\n") if s.strip()] if min((g for g in girinti if g > 0), default=4) != 4: bulgu.append("girinti adımı dört boşluk değil") for no, s in enumerate(kaynak.split("\n"), 1): if re.match(r"\s*(if|for|while) .*:\s*\S", s): bulgu.append(f"satır {no}: aynı satırda iki deyim") for op in ISLECLER: if op in s and f" {op} " not in s: bulgu.append(f"satır {no}: {op} çevresinde boşluk yok") return bulgu def yapi_bulgulari(kaynak): """Agaca bakan kurallar; bicimlendirici bunlari susturmaz.""" agac = ast.parse(kaynak) bulgu = [f"adlandırılmamış sabit {d.value}" for d in ast.walk(agac) if isinstance(d, ast.Constant) and isinstance(d.value, int) and d.value not in (0, 1)] karar = sum(1 for d in ast.walk(agac) if isinstance(d, ast.If)) return sorted(bulgu + ([f"karar sayısı {karar}, eşik 2"] if karar > 2 else [])) def bicimlendir(kaynak): """Bicimlendirici modeli: kaynak, agactan tek kurala gore yeniden yazilir.""" return ast.unparse(ast.parse(kaynak)) + "\n" def davranis(kaynak): ad = {} exec(compile(kaynak, "<yazım>", "exec"), ad) return tuple(ad["indirim"](100, u, k) for u in (False, True) for k in (False, True)) def dogru(tutar, uye, kupon): """Kahin: dogru davranis tavani 25 uygular.""" oran = min((10 if uye else 0) + (15 if kupon else 0), 25) return tutar - tutar * oran // 100 print(f"{'yazım':<17s} {'satır':>5s} {'düzen':>6s} {'yapı':>5s} {'davranış':>22s}") for ad, k in YAZIMLAR.items(): print(f"{ad:<17s} {len(k.strip().split(chr(10))):5d} " f"{len(duzen_bulgulari(k)):6d} {len(yapi_bulgulari(k)):5d} " f"{str(davranis(k)):>22s}") K = list(YAZIMLAR.values()) B = [bicimlendir(k) for k in K] once = {tuple(sorted(duzen_bulgulari(k) + yapi_bulgulari(k))) for k in K} sonra = {tuple(sorted(duzen_bulgulari(b) + yapi_bulgulari(b))) for b in B} print() print(f"ayrı kaynak metni {len(set(K))} | ayrı ayrıştırma ağacı " f"{len({ast.dump(ast.parse(k)) for k in K})} | ayrı davranış " f"{len({davranis(k) for k in K})} | biçimlendirmeden sonra ayrı metin " f"{len(set(B))}") print(f"tüy toplayıcı: biçimlendirmeden önce {len(once)} ayrı bulgu kümesi, " f"sonra {len(sonra)}") print(f"kalan bulgu {len(sorted(sonra)[0])}: {', '.join(sorted(sonra)[0])}") print() sapan = [(u, k) for u in (False, True) for k in (False, True) if davranis(KANONIK)[2 * u + k] != dogru(100, u, k)] print(f"dört girdi bileşiminden {len(sapan)} tanesinde davranış kâhinden " f"ayrılıyor: {sapan}")
yazım satır düzen yapı davranış dört boşluk 10 0 6 (100, 85, 90, 80) iki boşluk 10 1 6 (100, 85, 90, 80) sekiz boşluk 10 1 6 (100, 85, 90, 80) tek satır gövde 7 3 6 (100, 85, 90, 80) boşluksuz işleç 10 6 6 (100, 85, 90, 80) ikisi birden 7 10 6 (100, 85, 90, 80) ayrı kaynak metni 6 | ayrı ayrıştırma ağacı 1 | ayrı davranış 1 | biçimlendirmeden sonra ayrı metin 1 tüy toplayıcı: biçimlendirmeden önce 5 ayrı bulgu kümesi, sonra 1 kalan bulgu 6: adlandırılmamış sabit 10, adlandırılmamış sabit 100, adlandırılmamış sabit 15, adlandırılmamış sabit 20, adlandırılmamış sabit 20, karar sayısı 3, eşik 2 dört girdi bileşiminden 1 tanesinde davranış kâhinden ayrılıyor: [(True, True)]
Altı Yazım, Bir Sonuç
Alt satır dersin ana sayısını veriyor: 6 ayrı kaynak metni, 1 ayrı ayrıştırma ağacı, 1 ayrı davranış, biçimlendirmeden sonra 1 ayrı metin.
Üç sayının aynı anda okunması gerekir. Altı metin gerçekten ayrıdır — dosya olarak
karşılaştırılsalar altısı da birbirinden farklı çıkar, ve bir değişiklik kaydında altı ayrı
satır olarak görünürler. Buna karşın ağaç tektir: yorumlayıcının gördüğü şey altısında
da aynıdır. Davranış sütunu bunu bağımsız bir yoldan doğruluyor; altı yazımın altısı da
dört girdi bileşiminde (100, 85, 90, 80) veriyor.
Biçimlendiricinin yaptığı iş tam olarak bu üç sayı arasındaki boşluğu kapatmaktır. Ağaç zaten tekti; biçimlendirici metni de tekleştirir. Altıya bir kural uygulanır ve geriye 1 metin kalır. Bu, aracın verdiği tek güvencedir: aynı ağaçtan aynı metin.
Üst tablonun satır sütunu bedelin nerede olduğunu gösteriyor. Tek satır gövdeli yazımlar
işlevi 7 satıra sıkıştırıyor, öbürleri 10 satır tutuyor. Biçimlendirilmiş sonuç bu
seçimlerin hiçbirini korumaz; yazanın satır düzeni üzerindeki tercihi ortadan kalkar.
Aracın devraldığı şey tartışma değil, karardır.
Bu sayının pratik karşılığı, kaynağın karşılaştırılabilir hale gelmesidir. İki yazım arasındaki fark ağaca yansımıyorsa, o fark bir değişiklik kaydında yer kaplar ama hiçbir şey anlatmaz. Altı yazım biçimlendiriciden geçirildiğinde aradaki farkın tamamı sıfırlanır; geriye kalan her fark ağaca yansıyan bir farktır. Ölçüm bunu doğrudan söylüyor: ayrı metin sayısı 6’dan 1’e inerken ayrı ağaç sayısı zaten 1’di ve değişmedi.
Kuralın Tek Yerde Yazılması
Biçimlendiricinin verdiği güvence koşullu bir güvencedir: aynı kurala göre çalışan iki koşum aynı metni üretir. Kural değişirse çıktı da değişir. Bu yüzden aracın tek başına kullanılması yetmez; kuralın nerede yazıldığı sorusu ölçümün parçasıdır.
Kural üç ayrı yerde yaşayabilir. Yazan kişide yaşarsa, sonuç kişi sayısı kadar ayrı metindir — ölçümün başlangıç durumu tam olarak budur, altı yazım altı ayrı metin. Aracın öntanımlısında yaşarsa, sonuç aracın yapılandırmasına bağlı olur ve iki geliştirme ortamı ayrı öntanımlı taşıdığında sayı yine birden büyür. Projenin kendi dosyasında yaşarsa, kural kaynakla birlikte taşınır ve her koşum onu okur.
Üçüncüsü bu kursun bağımlılık tarafında kurduğu düzenle aynı biçimdedir. Kilit dosyası çözümlemeyi ortadan kaldırmıyordu; kararı bir kez verdirip yazıya geçiriyordu. Biçim kuralı da tartışmayı ortadan kaldırmaz; tartışmayı bir kez yaptırır, sonucu dosyaya yazar ve sonraki her koşumu o dosyaya bağlar. İkisinde de tekleşen şey işin kendisi değil, kararın kaynağıdır.
Aradaki fark, kararın ne kadar geri alınabilir olduğudur. Kilit dosyası değiştirildiğinde
kurulan sürümler değişir ve program başka kodla çalışır. Biçim kuralı değiştirildiğinde
üretilen metin değişir ama ağaç değişmez; ölçümün davranış sütunu bunu altı yazımın
altısında da gösteriyor. Biçim kararı bu yüzden ucuz bir karardır: yanlış verilirse
bedeli okunabilirliktir, doğruluk değil.
Tüy Toplayıcının Sustuğu Yer
düzen sütunu altı yazımda 0, 1, 1, 3, 6, 10 bulgu veriyor. Bu sayı yazımdan yazıma
değişiyor çünkü kural metne bakıyor. yapı sütunu altı yazımda da 6: ağaca bakan
kurallar düzeni hiç görmediği için yazımdan etkilenmiyor.
Sayıların nereden geldiği tek tek okunabilir. Tek satır gövdeli yazımda üç if satırı
“aynı satırda iki deyim” bulgusu üretiyor: 3. Boşluksuz işleç yazımında iki artırma
satırı, bir karşılaştırma satırı ve return satırındaki üç işleç bulgu veriyor: 6.
İkisi birden uygulandığında bunlar toplanıyor ve girinti adımı bulgusu ekleniyor: 10.
Bulgu sayısı, kaynağın kalitesiyle değil, kaynağın kanonik yazımdan ne kadar uzak
olduğuyla artıyor.
İkinci satır önemli bir ayrıntı taşıyor. Altı yazım 5 ayrı bulgu kümesi veriyor, 6 değil — çünkü iki boşluklu ve sekiz boşluklu yazımlar aynı tek bulguyu üretiyor: “girinti adımı dört boşluk değil”. Ayrı metin olmak ayrı bulgu kümesi olmayı gerektirmez. Bulgu kümesi kaynağın kaba bir özetidir ve iki ayrı kusuru aynı adla anabilir.
Biçimlendirmeden sonra bulgu kümesi sayısı 1’e iniyor ve o kümede 6 bulgu kalıyor:
beş adlandırılmamış sabit (10, 15, 20, 20, 100) ve bir karar sayısı bulgusu
(3, eşik 2). Biçimlendirici bunların hiçbirini susturmadı, çünkü hiçbiri düzenle ilgili
değil. Biçimlendirici bir kalite aracı değil, bir tekleştirme aracıdır.
Kalan altı bulgunun neden susturulamadığı da tek cümlede toplanır: hiçbirinin tek bir
doğru düzeltmesi yoktur. 20 sabitine bir ad vermek, adı seçmeyi gerektirir ve doğru adı
bilen şey kaynağın kendisi değil, alanın kuralıdır. Karar sayısını üçten ikiye indirmek
gövdeyi bölmeyi gerektirir ve bölmenin nereden yapılacağı ağaçtan okunamaz. Düzen
bulgularının hepsinde ise doğru sonuç tektir ve kuraldan çıkar — bu yüzden onlar bir araca
devredilebiliyor, öbürleri devredilemiyor.
Son satır ölçümün ucunu bağlıyor. Dört girdi bileşiminden 1’inde gerçek davranış kâhinden ayrılıyor: hem üyelik hem kupon verildiğinde işlev 80 döndürüyor, kâhin ise tavanı 25 uyguladığı için 75 bekliyor. Tüy toplayıcının bildirdiği bulgu sayısı 6, bu sapmayı bildiren bulgu sayısı 0.
Bu, önceki dersin sonucuyla aynı biçimdedir ve başka bir eksende durur. Kapsam kodun nerede yürüdüğünü saydı ve kusuru görmedi; tüy toplayıcı kodun nasıl yazıldığını denetliyor ve aynı kusuru yine görmüyor. İkisi de kâhini bilmez. Kâhini bilen tek şey testin beklentisidir, ve o beklenti bu kurguda kusurlu değeri yazmıştır.
Özet
- Biçimlendirici kaynağı yeniden yazar ve ayrı yazımları tek metne indirir; tüy toplayıcı kaynağa dokunmaz, bulgu bildirir.
- Aynı işlevin 6 ayrı yazımı 1 ayrıştırma ağacı ve 1 davranış verir; biçimlendirme sonrası ayrı metin sayısı 1’e iner.
- Metne bakan kurallar yazımdan yazıma 0 ile 10 arasında bulgu üretir; ağaca bakan kurallar altı yazımda da 6 bulguda sabit kalır.
- Bulgu kümesi sayısı biçimlendirmeden önce 5, sonra 1; ayrı metin olmak ayrı bulgu kümesi olmayı gerektirmez.
- Biçimlendirmeden sonra kalan 6 bulgunun hiçbiri kâhinden sapan tek çağrıyı bildirmez; biçim aracı bir kalite aracı değildir.
Sonraki Adım
Bu ders tek bir ortamda çalıştı: tek yorumlayıcı, tek bağımlılık kümesi, tek koşum. Ölçülen şey kaynağın kendisiydi ve kaynak ortamdan bağımsızdır. Testler için aynı şey söylenemez. Aynı takım, ortak tanımın gösterdiği gibi, yalnız sıraya göre bile ayrı karar verebiliyordu; sıra bir yapılandırma ayrıntısıydı. Yorumlayıcı sürümü ve çözümlenen bağımlılık kümesi de yapılandırma ayrıntısıdır. Sonraki ders bu ekseni açar: aynı takım kaç ortamda koşturulunca kaç ayrı karar veriyor, ve bu sayıyı hangi kararlar geri indiriyor?
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.