İçeriğe geç
academia.sh

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 node ile 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.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat