İçeriğe geç
academia.sh

Ders 01 / 22

Çalışma Ortamı

Bir defterin başka bir makinede aynı sayıyı vermesi için sabitlenmesi gereken kalemler sayılır: dokuz ortam kalemi ve M26/K03'ün on yedi adımlık hazırlama defteri karşılıklı yazıldığında hiçbir şey sabitlenmezken 17 adımın 2'si, bağımlılık listesiyle 5'i, kilit dosyasıyla 11'i yeniden üretilir; kalan 6 adım kilit dosyasının hiç yazmadığı beş kaleme bağlıdır. Aynı defter iki makinede koşturulduğunda yerel ayar 1.260 sayaç endeksinin tamamını değiştirir ve toplamı 1.236.664,22'den 41.433.984,33'e taşır, parça sayısı ortalamayı 0,006053 m³ oynatır (eşiğin 12 katı), defter hücre sırası 40 m³ üstü hücre sayısını 1.116'dan 1.264'e çıkarır.

İçindekiler

Veri Toplama ve Hazırlama kursu on yedi adımlık bir hazırlama defteriyle kapandı. O defterin ölçüsü geri okunabilirlikti: her adımın kararı ve satır hesabı çıktıya yazıldığında 39 kararın 39’u koşumdan sonra okunabilir oldu. Geri okunabilirlik bir defterin okunmasıyla ilgilidir ve kursun sorusunu tam olarak kapatmaz. Bu ders bir sonraki soruyu sorar: aynı defter başka bir makinede koşturulduğunda aynı sayıyı verir mi.

Soru araç seçmeden önce gelir. Bir dizi ya da veri çerçevesi arayüzü kullanılmaya başlandığı anda hesabın bir bölümü artık yazılan kodda değil, kurulan bileşenlerin içindedir; o bileşenlerin hangi sürümde olduğu, makinenin sayıyı hangi sözleşmeyle okuduğu ve hesabın kaça bölündüğü sonucu oynatır. Bu yüzden ortam burada bir kurulum komutu listesi olarak değil, sabitlenmesi gereken kalemlerin listesi olarak ele alınır. Ölçü de komut sayısı değil, kaç adımın iki makinede aynı sayıyı verdiğidir.

  • SH1. Bir defter, ürettiği sayıyı belirleyen bütün kalemler yazılıysa başka bir makinede aynı sayıyı verir. Kilit dosyası bu kalemlerin yalnız bir bölümünü yazar; kalanı defterin kendisi bildirmezse hiçbir yerde durmaz.
  • SH2. Ortamdan gelen bir kalem sayıyı iki biçimde oynatır: büyüklük düzeyinde, yani gözle görülür bir hatayla; ya da eşik civarında, yani yalnız karşılaştırıldığında görünen bir farkla. İkincisi satır sayısına bakan bir denetimden kaçar.

Ortamı Veri Olarak Yazmak

# ortam.py — MODELDIR: bir kosum ortami ve onu sabitleyen belgeler veri olarak yaziliyor.
# "duzey" kalemi hangi belgenin sabitledigidir. "adim" M26/K03'un on yedi adimli
# hazirlama defterinden okundu; yaninda o adimin sonucunu belirleyen kalemler var.
DUZEY = ["kurulu", "liste", "kilit", "bildirim"]
KALEM = [
    ("bagimlilik adi",             "liste"),
    ("dogrudan bagimlilik surumu", "kilit"),
    ("dolayli bagimlilik surumu",  "kilit"),
    ("yorumlayici surumu",         "kilit"),
    ("yerel ayar",                 "bildirim"),
    ("parca sayisi",               "bildirim"),
    ("rastgelelik tohumu",         "bildirim"),
    ("defter hucre sirasi",        "bildirim"),
    ("veri dosyasi ozeti",         "bildirim"),
]
ADIM = [
    ("01 dosya bicimleri",        ["bagimlilik adi", "yerel ayar"]),
    ("02 veritabanindan cekme",   ["bagimlilik adi", "veri dosyasi ozeti"]),
    ("03 arayuzlerden toplama",   ["bagimlilik adi"]),
    ("04 yapisal ayristirma",     ["bagimlilik adi", "dolayli bagimlilik surumu"]),
    ("05 ornekleme",              ["dogrudan bagimlilik surumu", "rastgelelik tohumu"]),
    ("06 eksik degerler",         ["bagimlilik adi"]),
    ("07 yinelenen kayitlar",     ["dogrudan bagimlilik surumu"]),
    ("08 tip donusumleri",        ["yorumlayici surumu", "yerel ayar"]),
    ("09 aykiri degerler",        ["dogrudan bagimlilik surumu"]),
    ("10 kategorik duzeltme",     ["dolayli bagimlilik surumu"]),
    ("11 metin normallestirme",   ["yorumlayici surumu"]),
    ("12 birlestirme",            ["dogrudan bagimlilik surumu", "defter hucre sirasi"]),
    ("13 yeniden sekillendirme",  ["bagimlilik adi"]),
    ("14 gruplama",               ["dogrudan bagimlilik surumu", "parca sayisi"]),
    ("15 turetilmis degiskenler", ["yorumlayici surumu"]),
    ("16 kategorik kodlama",      []),
    ("17 hazirlama defteri",      []),
]
SABIT = dict(KALEM)
sabitlenen = lambda d: {a for a, k in KALEM if DUZEY.index(k) <= DUZEY.index(d)}
ureten = lambda d: [ad for ad, ba in ADIM if set(ba) <= sabitlenen(d)]

gen = lambda w: lambda h: "".join(str(v).ljust(w[0]) if j == 0 else str(v).rjust(w[j])
                                  for j, v in enumerate(h))
y = gen([12, 16, 22, 16])
print(y(["duzey", "sabit kalem", "yeniden uretilen adim", "oynayan adim"]))
for d in DUZEY:
    u = ureten(d)
    print(y([d, f"{len(sabitlenen(d))}/{len(KALEM)}", f"{len(u)}/{len(ADIM)}",
             len(ADIM) - len(u)]))

kilit = set(ureten("kilit"))
print("\nkilit dosyasindan sonra oynayan adimlar ve nedeni:")
for ad, ba in ADIM:
    if ad not in kilit:
        print("  " + ad + " <- " + ", ".join(b for b in ba if SABIT[b] == "bildirim"))
duzey            sabit kalem yeniden uretilen adim    oynayan adim
kurulu                   0/9                  2/17              15
liste                    1/9                  5/17              12
kilit                    4/9                 11/17               6
bildirim                 9/9                 17/17               0

kilit dosyasindan sonra oynayan adimlar ve nedeni:
  01 dosya bicimleri <- yerel ayar
  02 veritabanindan cekme <- veri dosyasi ozeti
  05 ornekleme <- rastgelelik tohumu
  08 tip donusumleri <- yerel ayar
  12 birlestirme <- defter hucre sirasi
  14 gruplama <- parca sayisi

Dört düzeyin her biri farklı bir belgeye karşılık gelir. Kurulu düzeyinde hiçbir belge yoktur; bileşenler makineye sistem geneli kurulmuştur ve hangi sürümün orada olduğunu ancak makineye bakarak öğrenirsin. On yedi adımın yalnız 2’si iki makinede aynı sonucu verir — bunlar dış hiçbir kaleme dokunmayan adımlardır.

Yalıtılmış ortam bu düzeyin üstüne gelir ve tek bir şey yapar: defterin kullandığı bileşenleri makinenin geri kalanından ayırır. Yalıtım kendi başına bir sayı sabitlemez, sabitlemeyi mümkün kılar; ayrı bir kümede olmayan bir bileşenin sürümünü yazmanın anlamı yoktur, çünkü başka bir iş onu değiştirebilir.

Bağımlılık listesi defterin doğrudan kullandığı bileşenlerin adını kaydeder ve genellikle bir aralık verir. Bu, sabitlenen kalem sayısını 1’e çıkarır ve yeniden üretilen adımı 5’e taşır: yalnız davranışı sürümden sürüme değişmeyen adımlar kazanılır. Liste hangi bileşenin kullanıldığını söyler, hangi sürümünün kullanıldığını söylemez.

Kilit dosyası aradaki farkı kapatır. Doğrudan bileşenlerin tam sürümünü, onların çektiği dolaylı bileşenleri ve yorumlayıcının sürümünü tek bir çözümde yazar; ikinci makine aynı çözümü kurar. Sabitlenen kalem 4’e, yeniden üretilen adım 11’e çıkar. Kazanç burada bitiyor ve gizlenen karar tam bu noktada duruyor: kilit dosyası tam sürüm çözümü yazdığı için ortamın tamamının sabitlendiği sanılır. Sabitlenmeyen beş kalem 6 adımı oynatmaya devam eder ve bu altı adımın hiçbiri sürümle ilgili değildir.

Son düzey bir dosya değil, defterin kendi başında duran bir ortam bildirimidir: hangi yerel ayarla okunduğu, hesabın kaça bölündüğü, hangi tohumun kullanıldığı, hücrelerin hangi sırayla koştuğu ve girdi dosyasının özeti. Bu beş kalem yazıldığında oran 17’de 17 olur.

Dört belgenin işlevi birbirinin yerine geçmez ve bu ayrım kurulum komutlarından okunamaz. Yalıtılmış ortam bir sınır çizer, bağımlılık listesi bir niyet yazar, kilit dosyası o niyetin tek bir çözümünü kaydeder, ortam bildirimi ise çözümün dışında kalan koşum koşullarını yazar. Aradaki en sık karıştırılan ikili liste ile kilittir: liste insanın yazdığı ve elle güncellediği belgedir, kilit ise listeden türetilen ve elle yazılmayan belgedir. Listeyi daraltmak kilidin yerini tutmaz, çünkü dolaylı bileşenler listede hiç görünmez ve tablodaki 04 ile 10 numaralı adımlar tam olarak onlara bağlıdır.

Aynı Defter, İki Makine

# kosum.py — MODELDIR: ayni defter iki makinede kosturuluyor. Kurgu okuma kumesi
# K03'un hazirladigi kumedir: 1.260 abone, 3 donem, 3.780 hucrenin 3.199'u dolu.
def uretec(tohum):
    s = tohum & 0xFFFFFFFF
    def sonraki():
        nonlocal s
        s = (1664525 * s + 1013904223) & 0xFFFFFFFF
        return s / 4294967296
    return sonraki

ABONE, DONEM = 1260, 3
r = uretec(20260218)
HAM = [round(4 + r() * 56, 2) for _ in range(ABONE * DONEM)]
r2 = uretec(9110723)
p = [r2() for _ in range(ABONE * DONEM)]
BOS = set(sorted(range(len(p)), key=lambda j: p[j])[:581])
OKUMA = [None if j in BOS else HAM[j] for j in range(ABONE * DONEM)]
ortalama = lambda v: sum(v) / len(v)

# Kalem 5 — yerel ayar. Sayac endeksi metin olarak geliyor; ondalik ve binlik
# ayracinin hangisi oldugu makinenin yerel ayarindan okunuyor.
ENDEKS = [round(200 + (i * 7919) % 1400
                + sum(x for x in OKUMA[i*DONEM:(i+1)*DONEM] if x is not None), 2)
          for i in range(ABONE)]
def yaz(v):
    tam, _, kes = f"{v:.2f}".partition(".")
    g = ""
    while len(tam) > 3:
        tam, g = tam[:-3], "." + tam[-3:] + g
    return tam + g + "," + kes
oku = lambda m, binlik, ondalik: float(m.replace(binlik, "").replace(ondalik, "."))
METIN = [yaz(v) for v in ENDEKS]
A = [oku(m, ".", ",") for m in METIN]   # binlik nokta, ondalik virgul
B = [oku(m, ",", ".") for m in METIN]   # binlik virgul, ondalik nokta

# Kalem 6 — parca sayisi. Toplulastirma parcalara bolununce parca ortalamalarinin
# ortalamasi aliniyor; parcalar esit degil, cunku eksik hucre dagilimi esit degil.
def parcali_ortalama(k):
    sinir = [i * ABONE // k for i in range(k + 1)]
    parca = [[x for j in range(sinir[i], sinir[i+1]) for x in OKUMA[j*DONEM:(j+1)*DONEM]
              if x is not None] for i in range(k)]
    return ortalama([ortalama(x) for x in parca])
ESIK = 0.0005  # m3; okuma cozunurlugu 0,01 m3'un yirmide biri, altinda kalan fark okunamaz

# Kalem 8 — defter hucre sirasi. Iki hucre var: bos hucreyi abonenin kendi
# ortalamasiyla doldurmak ve ust tarife basamagini (40 m3) asan hucreyi saymak.
def doldur(v):
    o = []
    for i in range(ABONE):
        h = [v[i*DONEM+d] for d in range(DONEM) if v[i*DONEM+d] is not None]
        o.append(ortalama(h) if h else 0.0)
    return [o[j // DONEM] if x is None else x for j, x in enumerate(v)]
say = lambda v: sum(1 for x in v if x is not None and x > 40.0)

z = gen([22, 20, 20, 14])
print(z(["ortam kalemi", "makine A", "makine B", "ayni mi"]))
print(z(["yerel ayar (toplam)", round(sum(A), 2), round(sum(B), 2), "hayir"]))
print(z(["parca sayisi (ort.)", round(parcali_ortalama(1), 6), round(parcali_ortalama(8), 6),
         "evet" if abs(parcali_ortalama(1) - parcali_ortalama(8)) < ESIK else "hayir"]))
print(z(["hucre sirasi (40 ust)", say(OKUMA), say(doldur(OKUMA)), "hayir"]))

kucul = sum(1 for a, b in zip(A, B) if b < a)
fark = abs(parcali_ortalama(1) - parcali_ortalama(8))
print(f"\nyerel ayar: {len(METIN)} endeksin {len(METIN)}'i degisti; {kucul} tanesi binlik "
      f"ayraci ondalik sayildigi icin kuculdu, {len(METIN)-kucul} tanesi buyudu")
print(f"parca sayisi: fark {fark:.6f} m3, esik {ESIK} m3'un {fark/ESIK:.0f} kati")
print(f"hucre sirasi: ayni iki hucre, ters sirada {say(doldur(OKUMA))-say(OKUMA)} hucre fazla "
      f"sayildi (yuzde {100*(say(doldur(OKUMA))-say(OKUMA))/say(OKUMA):.0f})")
ortam kalemi                      makine A            makine B       ayni mi
yerel ayar (toplam)             1236664.22         41433984.33         hayir
parca sayisi (ort.)              31.895661           31.889608         hayir
hucre sirasi (40 ust)                 1116                1264         hayir

yerel ayar: 1260 endeksin 1260'i degisti; 613 tanesi binlik ayraci ondalik sayildigi icin kuculdu, 647 tanesi buyudu
parca sayisi: fark 0.006053 m3, esik 0.0005 m3'un 12 kati
hucre sirasi: ayni iki hucre, ters sirada 148 hucre fazla sayildi (yuzde 13)

Üç satırın üçü de aynı kodu, aynı veriyi ve aynı kilit dosyasını varsayar. Farkı üreten şey yalnız ortam kalemidir.

Birinci satır büyüklük düzeyinde bir farktır. Sayaç endeksi metin olarak geliyor ve iki sözleşme arasında seçim yapılıyor: ayracın hangisi ondalık, hangisi binlik. 1.260 endeksin tamamı değişiyor ve fark tek yönlü de değil — binlik ayracı taşıyan 613 endeks bin kat küçülüyor, ayraç taşımayan 647 endeks yüz kat büyüyor. Toplam 1.236.664,22 m³ yerine 41.433.984,33 m³ çıkıyor. Bu tür bir hata gözle görülür, ama görülmesi için birinin iki makinede birden bakması gerekir.

İkinci satır tam tersidir. Toplulaştırma sekiz parçaya bölündüğünde parça ortalamalarının ortalaması alınıyor; parçalar eşit büyüklükte değil, çünkü eksik hücreler abonelere eşit dağılmamış. Sonuç 31,895661 yerine 31,889608 m³. Kayan noktalı iki sayı == ile karşılaştırılmaz; bunun yerine eşik yazılır. Buradaki eşik 0,0005 m³, yani sayaç çözünürlüğü olan 0,01 m³’ün yirmide biri: bu değerin altındaki bir fark ölçüm aygıtından zaten okunamaz, dolayısıyla aynı sayılır. Fark 0,006053 m³ ile eşiğin 12 katı ve eşiğin üstünde kaldığı için iki koşum aynı sayıyı vermiyor. Satır sayısı ise iki koşumda da 3.199; satır sayısına bakan bir denetim bu farkı görmez.

Üçüncü satır defterin biçimiyle ilgilidir. İki hücre var: boş hücreyi abonenin kendi ortalamasıyla doldurmak ve üst tarife basamağını aşan hücreleri saymak. Doldurma önce koşarsa 581 hücre bir değer kazanır ve bunların bir bölümü 40 m³ eşiğini geçer; sayım 1.116 yerine 1.264 çıkar, 148 hücre fazla, yüzde 13. Hiçbir hücre yanlış yazılmamıştır; yalnız sıraları farklıdır.

Defterin Kaydettiği ve Kaydetmediği

Bu son satır defter biçiminin neden bir ortam kalemi olduğunu gösteriyor. Bir defter dosyası hücreleri yazıldıkları sırayla saklar, koştukları sırayla değil; her hücrenin yanında yalnız bir koşum sayacı durur. Sayaç 3, 1, 2 ise hücreler yukarıdan aşağıya okunduğunda doğru görünen bir defter, gerçekte başka bir sırayla koşmuş demektir. Bu yüzden bir defterin yeniden üretilebilir sayılması için tek koşullu bir kural yazılır: defter baştan sona, tek seferde, temiz bir durumda koşturulduğunda aynı çıktıyı vermelidir. Kaydedilmiş çıktı bu kuralın kanıtı değildir, çünkü kaydedilmiş çıktı hangi sırayla üretildiğini söylemez.

Ölçünün bu kursta neden ilk derste kurulduğu da buradan görünüyor. Bundan sonraki derslerde hesabın gövdesi elle yazılan döngülerden çıkıp bir soyutlamanın içine girecek; o soyutlamanın öğe tipi, biçimi ve toplama sırası hakkında verdiği kararlar tam olarak yukarıdaki tabloya yeni satırlar eklemektir. Sekiz parçalı toplulaştırmanın ortalamayı 0,006053 m³ oynatması bir hata değil, bir varsayılandır; kim kaça böleceğine karar verdiyse o sayıyı da o vermiştir. Ortam bildirimi bu yüzden sürüm listesinin uzantısı değil, hesabın bir parçasıdır.

Aynı gerekçe kaydedilen çıktıya da uygulanır. Defter hem kodu hem çıktıyı tek dosyada tuttuğu için okuması kolaydır, ama iki koşum arasındaki farkın nerede başladığını göstermez; bunu gösteren şey her adımın kendi kararlarını ve satır hesabını yazdığı koşum defteridir. Kilit dosyası ortamı, koşum defteri kararı, defterin baştan koşturulması da sırayı sabitler; üçü birden yazılmadan “aynı sayı” bir iddia olarak kalır.

Özet

  • Bir çalışma ortamı komut listesi değil, sabitlenmesi gereken dokuz kalemin listesidir; her kalemi farklı bir belge sabitler.
  • Hiçbir belge yokken 17 adımın 2’si, bağımlılık listesiyle 5’i, kilit dosyasıyla 11’i iki makinede aynı sonucu verir.
  • Kilit dosyasından sonra oynayan 6 adımın hiçbiri sürümle ilgili değildir; yerel ayar, parça sayısı, tohum, hücre sırası ve veri özeti kilit dosyasında yazılı değildir.
  • Yerel ayar farkı 1.260 endeksin tamamını değiştirir ve toplamı 1.236.664,22’den 41.433.984,33’e taşır; 613 endeks küçülür, 647 endeks büyür.
  • Parça sayısı ortalamayı 0,006053 m³ oynatır — 0,0005 m³ eşiğinin 12 katı — ama satır sayısını hiç değiştirmez; hücre sırası 40 m³ üstü hücre sayısını 1.116’dan 1.264’e çıkarır.

Sonraki Adım

Ortam sabitlendiğinde defter iki makinede aynı sayıyı verir, ama hesabın kendisi hâlâ önceki kursta olduğu gibi elle yazılmış döngülerle yapılıyor. Bu bölümün kod bloklarında 3.780 hücre tek tek gezildi, her hücre ayrı bir nesne olarak tutuldu ve her toplama işi için veri baştan sona yeniden okundu. Sonraki ders aynı işi bir dizi arayüzüyle yapar ve kursun ölçü birimini kurar: aynı toplama işi kaç satır kod, kaç geçiş ve kaç bayt tutuyor.

İ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