İçeriğe geç
academia.sh

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.
  • KY6indirim iş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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat