Ders 08 / 17
Tip Dönüşümleri
Devralınan 1.880 satırda okuma alanının 542'si virgüllü, endeks alanının 407'si binlik ayraçlı yazılmıştır. Yaygın sayıya çevirme çağrısı hiçbir hata üretmez ve hiçbir işlemi durdurmaz, ama 827 satırı bozar: 541 okuma ile 407 endeks yanlış değere döner, raporlanan tüketim toplamı 263,6 m³ eksik çıkar ve ortalama endeks 13.481'den 10.558'e iner. Tarih alanında biçimi tanımayan bir ayrıştırıcı 1.880 satırın 1.054'ünü doğru çözer, 361'ini yanlış aya yazar, 465'ini hiç çözemez ve bu satırlar tarih süzgecinden sessizce düşer; ocak döneminde yapılan ziyaret sayısı 1.388 yerine 805 raporlanır. Biçimi önce tanıyan dönüşüm aynı kümede sıfır bozuk kayıt ve sıfır kayıp satır verir.
İçindekiler
Bir önceki ders kümeyi 1.880 tekil satıra indirdi. Bu satırların okuma, endeks ve tarih alanları hâlâ metindir ve hesaba girmeleri için çevrilmeleri gerekir.
Dönüşüm bu konudaki en sessiz adımdır. Silme satır sayısını düşürür ve bir şeyin eksildiği görülür. Dönüşüm ise hiçbir iz bırakmaz: yaygın bir sayıya çevirme çağrısı her metinden bir sayı çıkarır, hata vermez, işlemi durdurmaz ve sonuç kümesi düzgün görünür. Dersin sayısı budur: kaç kayıt hatasız görünerek bozuldu ve kaç satır hiç haber verilmeden düştü.
- VZ9. Küme bir önceki dersten devralınır: 188 satır birleşmiş, 1.880 tekil satır kalmıştır.
Aynı tohum, aynı satırlar; küme
nodeile modellenir ve kurgudur. - VZ10. Aynı sayı iki ayraç geleneğiyle yazılabilir: okuma alanında ondalık ayracı nokta ya da virgül, endeks alanında binlik ayracı yok ya da noktadır. Aynı karakter iki farklı iş yapar.
- VZ11. Sessiz dönüşüm, hata üretmeden yanlış değer üreten dönüşümdür. Bozulan kayıt, iki dönüşümün sonucu arasındaki farkın 0,005 eşiğini aşmasıyla sayılır; kayan noktalı iki değer eşitlik işleciyle karşılaştırılmaz.
- VZ12. Çözülemeyen bir tarih hata değil, süzgeçten düşen bir satırdır: uyarı üretmez, yalnız sonraki sayımda görünmez.
Sayıya Çevirme
// tip.mjs — ayni ham kume MODELIDIR: her alan saha formundan geldigi haliyle // metindir. Kume kurgudur: bolgesel olcum aginin 1400 abonesi ve saha ziyaret // kayitlari; ayni tohum, ayni satirlar. export const TOHUM = 20260305, ABONE = 1400; const karistir = (x) => { x = Math.imul(x ^ (x >>> 16), 2246822507); x = Math.imul(x ^ (x >>> 13), 3266489909); return (x ^ (x >>> 16)) >>> 0; }; export const uretec = (t) => { let s = karistir(t); return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; export const ayrik = (u, w) => { let t = 0; for (let i = 0; i < w.length; i++) { t += w[i]; if (u < t) return i; } return w.length - 1; }; // Son alan: ziyaretin okuma uretmeme orani. Bolgeler bu oranda ayrisir. export const BOLGE = [["kuzey", "KZ", 0.28, 21, 0.06], ["guney", "GN", 0.22, 17, 0.03], ["dogu", "DG", 0.18, 26, 0.27], ["bati", "BT", 0.14, 14, 0.03], ["merkez", "MR", 0.18, 23, 0.08]] .map(([ad, kod, pay, taban, eksik]) => ({ ad, kod, pay, taban, eksik })); const SOKAK = ["Çınar", "Söğüt", "Işık", "Ihlamur", "Zeytin", "Menekşe", "Palmiye", "Ceviz"]; const TARIFE = ["Mesken", "mesken", "MESKEN", " Mesken ", "Konut", "konut", "Ticari", "ticari", "TİCARİ", "Is yeri", "İş Yeri", "isyeri", "Sanayi", "sanayi", "Tarimsal"]; export const HAM = []; let bir = null; for (let i = 0; i < ABONE; i++) { const r = uretec(TOHUM + i); const b = BOLGE[ayrik(r(), BOLGE.map((x) => x.pay))]; const taban = Math.max(1.2, b.taban * Math.exp((r() + r() + r() - 1.5) * 0.62)); const sayac = `${b.kod}-${String(1000 + i).padStart(4, "0")}`; const paylas = bir && bir.b === b.ad && r() < 0.03; // ayni binada ikinci sayac const sk = paylas ? bir.sk : SOKAK[Math.floor(r() * SOKAK.length)]; const kapi = paylas ? bir.kapi : 1 + Math.floor(r() * 60); const daire = paylas ? bir.daire : r() < 0.26 ? `/${1 + Math.floor(r() * 9)}` : ""; bir = { b: b.ad, sk, kapi, daire }; const endeks0 = 8000 + Math.floor(r() * 11000); for (const d of r() < 0.34 ? ["2026-01", "2026-02"] : ["2026-01"]) { const q = uretec(TOHUM + 7000 + HAM.length), u = q(); const gercek = Math.round(taban * (d === "2026-02" ? 0.94 : 1) * 100) / 100; // Eksiklik uc bicimde gorunur: hucre bos, hucrede yalniz bosluk, "yok" yazili. const eksik = u < b.eksik ? ayrik(q(), [0.52, 0.19, 0.29]) : -1; const kayma = eksik < 0 && q() < 0.008; // ondalik kaymasi: okuma on kat yazilmis const yazilan = kayma ? Math.round(gercek * 1000) / 100 : gercek; const okuma = eksik === 0 ? "" : eksik === 1 ? " " : eksik === 2 ? "yok" : q() < 0.31 ? yazilan.toFixed(2).replace(".", ",") : yazilan.toFixed(2); const gun = 1 + Math.floor(q() * 27), ay = d.slice(5); const tb = ayrik(q(), [0.55, 0.21, 0.24]); const tarih = tb === 0 ? `${d}-${String(gun).padStart(2, "0")}` : tb === 1 ? `${String(gun).padStart(2, "0")}.${ay}.2026` : `${String(gun).padStart(2, "0")}/${ay}/2026`; const endeks = endeks0 + Math.round(gercek * (d === "2026-02" ? 2 : 1)); const eb = q() < 0.22; const av = ayrik(q(), [0.30, 0.18, 0.18, 0.16, 0.18]); const kap = `${kapi}${daire}`; const M = `${b.ad[0].toUpperCase()}${b.ad.slice(1)}`, mUst = b.ad.toLocaleUpperCase("tr"); const alt = sk.toLocaleLowerCase("tr"), ust = sk.toLocaleUpperCase("tr"); const adres = av === 0 ? `${b.ad} mahallesi ${alt} sokak no: ${kap}` : av === 1 ? `${M} Mahallesi ${sk} Sokak No ${kap}` : av === 2 ? `${mUst} MAHALLESİ ${ust} SOKAK NO ${kap}` : av === 3 ? ` ${M} Mahallesi ${sk} Sokak No ${kap} ` : `${b.ad} mah. ${alt} sk. no:${kap}`; HAM.push({ abone_no: 10001 + i, sayac_no: q() < 0.011 ? "" : sayac, bolge: b.ad, donem: d, okuma_metni: okuma, endeks_metni: eb ? `${Math.floor(endeks / 1000)}.${String(endeks % 1000).padStart(3, "0")}` : `${endeks}`, tarih_metni: tarih, tarife_metni: TARIFE[ayrik(q(), Array(15).fill(1 / 15))], adres_metni: adres, gercek_m3: gercek, yazilan_m3: eksik < 0 ? yazilan : null }); if (q() < 0.09) { // ayni ziyaret ikinci kez girilmis const on = HAM[HAM.length - 1], bicim = ayrik(q(), [0.34, 0.28, 0.38]); HAM.push({ ...on, sayac_no: bicim === 0 ? ` ${sayac}` : bicim === 1 ? sayac.toLowerCase() : sayac, adres_metni: av === 2 ? `${b.ad} mah. ${alt} sk. no:${kap}` : `${mUst} MAHALLESİ ${ust} SOKAK NO ${kap}`, tarife_metni: TARIFE[ayrik(q(), Array(15).fill(1 / 15))] }); } } } // Okumayi sayiya cevirir. Ondalik ayraci virgul de olabilir; donusumun kendi // tuzaklari bu konunun ucuncu dersinde olculur, burada tolere edilir. export const sayiya = (t) => { const s = String(t).trim().replace(",", "."); return s === "" || !/^\d+(\.\d+)?$/.test(s) ? null : Number(s); }; export const medyan = (a) => { const l = [...a].sort((x, y) => x - y); return l.length % 2 ? l[(l.length - 1) / 2] : (l[l.length / 2 - 1] + l[l.length / 2]) / 2; }; export const y = (w) => (h) => h.map((v, j) => (j ? String(v).padStart(w[j]) : String(v).padEnd(w[0]))).join(""); // Onceki dersin karari: kimlik alani bos olan satirlar elendi, eksik okumali // satirlar isaretlenip bolge ortancasiyla dolduruldu. Bu adima 2068 satir girer. const kimlikli = HAM.filter((s) => s.sayac_no.trim() !== ""); const olculen = kimlikli.filter((s) => sayiya(s.okuma_metni) !== null); const bolgeMed = new Map(BOLGE.map((b) => [b.ad, medyan(olculen .filter((s) => s.bolge === b.ad).map((s) => sayiya(s.okuma_metni)))])); export const GIREN = kimlikli.map((s) => sayiya(s.okuma_metni) === null ? { ...s, okuma_metni: bolgeMed.get(s.bolge).toFixed(2) } : s); // Onceki dersin karari: abone, donem ve duzeltilmis sayac kodundan uretilen // anahtarla tekillestirildi; 188 satir birlesti. Bu adima 1880 satir girer. const tekille = (l, k) => { const m = new Map(); for (const s of l) if (!m.has(k(s))) m.set(k(s), s); return [...m.values()]; }; export const GIREN2 = tekille(GIREN, (s) => `${s.abone_no}|${s.donem}|${s.sayac_no.trim().toUpperCase()}`); // Iki donusum karari. D1 yaygin cagridir: hicbir hata uretmez, her metinden bir // sayi cikarir. D2 bicimi once tanir, tanimadigi metni sayiya cevirmez. export const D1 = { okuma: (t) => parseFloat(t), endeks: (t) => parseFloat(t) }; export const D2 = { okuma: (t) => Number(t.trim().replace(",", ".")), endeks: (t) => Number(t.replace(".", "")) }; export const topla = (a) => a.reduce((x, z) => x + z, 0); export const ESIK = 0.005; // raporlanan iki ondalik basamagin yarisi if (import.meta.url === `file://${process.argv[1]}`) { const say = (f) => GIREN2.filter(f).length; console.log(`model (tohum ${TOHUM}): onceki adimdan devralinan kume ${GIREN2.length} satir`); const y1 = y([36, 8]); console.log("\n" + y1(["alanin yazim bicimi", "satir"])); console.log(y1(["okuma, ondalik ayraci nokta", say((s) => !s.okuma_metni.includes(","))])); console.log(y1(["okuma, ondalik ayraci virgul", say((s) => s.okuma_metni.includes(","))])); console.log(y1(["endeks, binlik ayraci yok", say((s) => !s.endeks_metni.includes("."))])); console.log(y1(["endeks, binlik ayraci nokta", say((s) => s.endeks_metni.includes("."))])); const y2 = y([32, 10, 9, 13, 12]); console.log("\n" + y2(["sayisal donusum", "bozulan", "hata", "toplam m3", "ort endeks"])); for (const [ad, d] of [["D1 yaygin cagri", D1], ["D2 bicim farkinda", D2]]) { const bozuk = GIREN2.filter((s) => Math.abs(d.okuma(s.okuma_metni) - D2.okuma(s.okuma_metni)) > ESIK || Math.abs(d.endeks(s.endeks_metni) - D2.endeks(s.endeks_metni)) > ESIK).length; console.log(y2([ad, bozuk, 0, topla(GIREN2.map((s) => d.okuma(s.okuma_metni))).toFixed(1), (topla(GIREN2.map((s) => d.endeks(s.endeks_metni))) / GIREN2.length).toFixed(0)])); } const fark = topla(GIREN2.map((s) => D2.okuma(s.okuma_metni) - D1.okuma(s.okuma_metni))); console.log(`\nD1 hicbir hata uretmedi ve hicbir islem durmadi; okuma alaninda ` + `${say((s) => Math.abs(D1.okuma(s.okuma_metni) - D2.okuma(s.okuma_metni)) > ESIK)} kaydi, ` + `endeks alaninda ${say((s) => Math.abs(D1.endeks(s.endeks_metni) - D2.endeks(s.endeks_metni)) > ESIK)} kaydi bozdu. Raporlanan tuketim toplami ` + `${fark.toFixed(1)} m3 eksik cikiyor (karsilastirma ${ESIK} m3 esigiyle yapilir, ` + `kayan noktali degerler esitlik islecine sokulmaz)`); }
model (tohum 20260305): onceki adimdan devralinan kume 1880 satir alanin yazim bicimi satir okuma, ondalik ayraci nokta 1338 okuma, ondalik ayraci virgul 542 endeks, binlik ayraci yok 1473 endeks, binlik ayraci nokta 407 sayisal donusum bozulan hata toplam m3 ort endeks D1 yaygin cagri 827 0 41511.2 10558 D2 bicim farkinda 0 0 41774.8 13481 D1 hicbir hata uretmedi ve hicbir islem durmadi; okuma alaninda 541 kaydi, endeks alaninda 407 kaydi bozdu. Raporlanan tuketim toplami 263.6 m3 eksik cikiyor (karsilastirma 0.005 m3 esigiyle yapilir, kayan noktali degerler esitlik islecine sokulmaz)
İlk tablo alanların nasıl yazıldığını sayıyor: okumaların 542’sinde ondalık ayracı virgüldür, endekslerin 407’sinde binlik ayracı noktadır; ikisi de tek bir alanın içinde, formu dolduran kişinin alışkanlığına göre karışır.
İkinci tablo dersin sayısıdır. D1 yaygın çağrıdır ve hiçbir hata üretmez — hata sütunu iki satırda da sıfırdır. Yine de 827 satırı bozar. Virgüllü bir okuma metninde çağrı virgüle kadar okur ve durur: 12,74 değeri 12 olur, ondalık kısım sessizce atılır. Binlik ayraçlı bir endekste ise nokta ondalık ayracı sanılır: 12.480 değeri 12,48 olur. Birincisi küçük bir yanlışlıktır, ikincisi bin kat yanlıştır ve ikisi de aynı sessizlikle olur.
Toplamlar bunu doğruluyor. Raporlanan tüketim toplamı 263,6 m³ eksik çıkar — kimsenin fark etmeyeceği bir sapma. Ortalama endeks ise 13.481’den 10.558’e iner; endeks tüketim farkını hesaplamakta kullanılan alandır ve bu kadar bozulmuş bir alanla yapılacak her fark hesabı anlamsızdır. D2 sıfır bozuk kayıt verir; tek yaptığı, çevirmeden önce biçimi tanımaktır.
Tarihe Çevirme
// tarih.mjs — ayni kume, bu kez tarih alani. Iki ayristirma karari: biri // yaziliş bicimini bilmeden ilk alani ay sayar, digeri bicimi once tanir. import { GIREN2, y } from "./tip.mjs"; // T1: bicim bilinmiyor, ilk alan ay sayiliyor (dort haneli ilk alan yil demektir). // T2: bicim once taninir; ayracli yazimda alanlar gun, ay, yil sirasindadir. const T1 = (t) => { const p = t.split(/[-./]/).map(Number); const [yil, ay, gun] = p[0] > 999 ? p : [p[2], p[0], p[1]]; return ay >= 1 && ay <= 12 && gun >= 1 && gun <= 31 ? { yil, ay, gun } : null; }; const T2 = (t) => { const p = t.split(/[-./]/).map(Number); const [yil, ay, gun] = /^\d{4}-/.test(t) ? p : [p[2], p[1], p[0]]; return ay >= 1 && ay <= 12 && gun >= 1 && gun <= 31 ? { yil, ay, gun } : null; }; const iki = (n) => String(n).padStart(2, "0"); const y1 = y([26, 9, 11, 11, 11]); console.log(y1(["tarih ayristirma", "gecersiz", "yanlis ay", "dogru", "hata"])); for (const [ad, f] of [["T1 bicim bilinmiyor", T1], ["T2 bicim taninir", T2]]) { let g = 0, ya = 0, d = 0; for (const s of GIREN2) { const t = f(s.tarih_metni); if (t === null) g++; else if (iki(t.ay) !== s.donem.slice(5)) ya++; else d++; } console.log(y1([ad, g, ya, d, 0])); } // Ayni kumeden raporlanan sayi: ocak doneminde yapilan ziyaret sayisi. const ocak = GIREN2.filter((s) => s.donem === "2026-01").length; const t1Ocak = GIREN2.filter((s) => { const t = T1(s.tarih_metni); return t !== null && t.ay === 1; }).length; const t1Gecerli = GIREN2.filter((s) => T1(s.tarih_metni) !== null).length; console.log(`\nocak doneminde yapilan ziyaret: donem alanina gore ${ocak}, ` + `T1'in cozdugu tarihe gore ${t1Ocak}; T1 ${GIREN2.length - t1Gecerli} satiri hic ` + `cozemedi ve bu satirlar tarih suzgecinden sessizce dustu`); const y2 = y([22, 10, 16]); console.log("\n" + y2(["tarihin yazim bicimi", "satir", "T1 cozer mi"])); for (const [ad, f, c] of [["yil-ay-gun", (t) => /^\d{4}-/.test(t), "evet"], ["gun.ay.yil", (t) => t.includes("."), "gun <= 12 ise"], ["gun/ay/yil", (t) => t.includes("/"), "gun <= 12 ise"]]) console.log(y2([ad, GIREN2.filter((s) => f(s.tarih_metni)).length, c])); console.log(`\nsatir hesabi: giren ${GIREN2.length}, cikan ${GIREN2.length}; 0 satir elendi, ` + `0 satir sessizce dustu. Yaygin donusum secilseydi ${GIREN2.length - t1Gecerli} satir ` + `sessizce duser, 948 alan hatasiz gorunerek bozulur ve hicbir uyari cikmazdi`);
tarih ayristirma gecersiz yanlis ay dogru hata T1 bicim bilinmiyor 465 361 1054 0 T2 bicim taninir 0 0 1880 0 ocak doneminde yapilan ziyaret: donem alanina gore 1388, T1'in cozdugu tarihe gore 805; T1 465 satiri hic cozemedi ve bu satirlar tarih suzgecinden sessizce dustu tarihin yazim bicimi satir T1 cozer mi yil-ay-gun 1022 evet gun.ay.yil 389 gun <= 12 ise gun/ay/yil 469 gun <= 12 ise satir hesabi: giren 1880, cikan 1880; 0 satir elendi, 0 satir sessizce dustu. Yaygin donusum secilseydi 465 satir sessizce duser, 948 alan hatasiz gorunerek bozulur ve hicbir uyari cikmazdi
Tarih alanı aynı sorunun daha sert biçimidir, çünkü burada yanlış değer bazen hiçbir değer olur. T1 yazılış biçimini bilmez ve yaygın bir varsayımı uygular: ilk alan aydır. 1.880 satırın 1.054’ünü doğru çözer, 361’ini yanlış aya yazar ve 465’ini hiç çözemez.
Üç sonuç farklı davranır. Yanlış aya yazılan satır kümede kalır, düzgün bir tarih taşır ve yanlış olduğu hiçbir yerden anlaşılmaz — 1 Şubat’ta yapılan bir ziyaret 2 Ocak’ta yapılmış görünür. Çözülemeyen satır ise hata mesajı üretmez, yalnız tarihe göre süzen ilk işlemde listeden çıkar.
Raporlanan sayı bu üçünün toplamıdır. Ocak döneminde yapılan ziyaret sayısı dönem alanına göre 1.388, T1’in çözdüğü tarihlere göre 805’tir. Aradaki 583 ziyaretin bir bölümü yanlış aya yazılmış, bir bölümü görünmez olmuştur.
Adımın hesabı şudur: giren 1.880 satır, çıkan 1.880 satır. Hiçbir satır elenmedi, hiçbir satır sessizce düşmedi, hiçbir kayıt bozulmadı. Bu adımın bedeli, yapılmadığında ortaya çıkan sayıdır: yaygın dönüşüm seçilseydi 465 satır sessizce düşer, 948 alan hatasız görünerek bozulurdu.
Özet
- Aynı alan iki ayraç geleneğiyle yazılabilir: 1.880 satırın 542’sinde ondalık ayracı virgül, 407’sinde binlik ayracı noktadır.
- Yaygın sayıya çevirme çağrısı hiçbir hata üretmez ve 827 satırı bozar: virgüllü okuma ondalık kısmını yitirir, binlik ayraçlı endeks bin kat küçülür.
- Etkisi alana göre değişir: tüketim toplamı 263,6 m³ eksik çıkar, ortalama endeks 13.481’den 10.558’e iner.
- Biçimi tanımayan bir tarih ayrıştırıcısı 1.054 satırı doğru çözer, 361’ini yanlış aya yazar, 465’ini hiç çözemez; ocak ziyaretleri 1.388 yerine 805 raporlanır.
- Adımın hesabı: giren 1.880, çıkan 1.880; 0 elendi, 0 sessizce düştü, 0 kayıt bozuldu — yaygın dönüşüm seçilseydi 465 satır düşer ve 948 alan bozulurdu.
Sonraki Adım
Bütün alanlar artık doğru tiptedir ve 1.880 okumanın hepsi bir sayıdır. Sayı olmaları doğru oldukları anlamına gelmiyor: bir bölümü ondalık noktası kayarak on kat büyük yazılmıştır ve dönüşüm bunları sadakatle çevirmiştir. Sonraki ders bu değerleri çeyrekler açıklığı ve standart puanla tarar, iki yöntemin işaretlediği kümelerin örtüşmeyen kısmını sayar ve işaretlenenleri ayırmanın ortalamayı ne kadar oynattığını ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.