Ders 04 / 11
Metin İşlevleri
48 saha ziyareti kaydı 60 abonelik listeyle abone kodu üzerinden eşleştiriliyor ve ham hâlinde yalnız 8'i tutuyor. Kırpma 24'e, kırpmayla birlikte büyük harfe çevirme 32'ye, her tür boşluğu silmek 48'e çıkarıyor; rapor edilen ziyaret tüketimi bu kararlarla 232,1 m³ ile 1204,8 m³ arasında oynuyor. Kodu yeniden kuran beşinci karar aynı 48'i verirken başka bir önekle gelen kaydı sessizce yanlış aboneye bağlıyor. Dil duyarsız harf dönüşümü teknisyen adlarının 15'ini tutturamıyor ve aynı sonucu üreten üç yazımdan yalnız birinde 40 düzeltme kararı hücre hücre geri okunabiliyor.
İçindekiler
Buraya kadar her karşılaştırma sayısaldı. Sayfanın öteki yarısı metindir ve metinde eşitlik
sayıdaki kadar açık değildir: AB-1041 ile AB-1041 ekranda aynı görünür, aynı uzunlukta
görünmez ve karşılaştırıldığında eşit çıkmaz. Bu ders saha ziyareti kayıtlarını abone
listesiyle abone kodu üzerinden eşleştirir ve kaç kaydın birleşmediğini sayar.
Ölçü iki yerde okunur. Birincisi eşleşen kayıt sayısı ve onunla birlikte rapora giren tüketim değeridir. İkincisi temizleme kararının nerede durduğudur: bir yardımcı sütunda mı, eşleştirme formülünün içinde mi, yoksa hiçbir yerde mi — çünkü elle düzeltilmiş bir hücre sonucu doğru gösterir ve kararı yok eder.
- TT13. Ziyaret tablosundaki abone kodları altı biçimde gelir: temiz, sonunda boşluk,
başında boşluk, küçük harfli, sonunda görünmeyen boşluk, ortasında boşluk. Bozulmalar
kurgudur ve kendi tohumuyla sırayla dağıtılır. Bozulmanın türü sayfada yazmaz; aşağıdaki
turişlevi onu ancak inceleyerek sınıflandırır. - TT14.
kirpişlevi yalnız normal boşluğu siler; görünmeyen boşluk ile iç boşluk kırpılmadan kalır.temizleher tür boşluğu siler. İkisi arasındaki fark bu dersin en pahalı kararıdır. - TT15.
buyukdil duyarsız bir harf dönüşümüdür;buyuktrTürkçenin i harfi kuralını uygular. Ekip adları kurgudur ve yarısı küçük harfle girilmiştir.
Gözle Aynı, Karşılaştırmada Farklı
Aşağıdaki blok iki tabloyu kurar: soldaki 60 abonelik liste, sağdaki 48 ziyaret kaydı. Çıktıda boşluklar görünür karakterlere çevrilir — normal boşluk alt çizgi, görünmeyen boşluk tilde.
// metin.mjs — elektronik tablo MODELI: 01'in modeli, metin islevleriyle genisletilmis. // Saha ziyaretleri tablosundaki abone kodlari bozuk gelir; bozulmalar kurgudur. export const TOHUM = 20260602, SATIR = 60, ZIYARET = 48; const uretec = (t) => { let s = t >>> 0; return () => (s = (s * 1664525 + 1013904223) >>> 0) / 4294967296; }; export const GB = "\u00a0"; // gorunmeyen bosluk: hucrede normal boslukla ayni const REF = /(\$?)([A-Z])(\$?)(\d+)/g; export const kaydir = (f, dS, dR) => f.replace(REF, (_, a, s, b, r) => (a ? "$" + s : String.fromCharCode(s.charCodeAt(0) + dS)) + (b ? "$" + r : String(+r + dR))); // Metin islevleri. buyuk dil duyarsizdir; buyuktr Turkce i/I kuralini uygular. const ISLEV = { kirp: (s) => String(s).replace(/^ +| +$/g, ""), // yalniz normal bosluk kirpilir buyuk: (s) => String(s).toUpperCase(), buyuktr: (s) => String(s).replace(/i/g, "İ").replace(/ı/g, "I").toUpperCase(), temizle: (s) => String(s).replace(/\s/g, ""), // \s gorunmeyen boslugu da kapsar birlestir: (...p) => p.map(String).join(""), sagdan: (s, n) => String(s).slice(-n), // sondan n karakter: kodun sayi kismi uzunluk: (s) => String(s).length, }; export function deger(H, ad) { const v = H[ad]; if (v === undefined) return 0; if (typeof v !== "string" || v[0] !== "=") return v; const ifade = v.slice(1).replace(REF, (m) => JSON.stringify(deger(H, m.replace(/\$/g, "")))); return Function(...Object.keys(ISLEV), `return (${ifade});`)(...Object.values(ISLEV)); } // Alti bozulma turu; sayfada gozle ayirt edilemezler. export const BOZ = [["temiz", (k) => k], ["sondaki bosluk", (k) => k + " "], ["bastaki bosluk", (k) => " " + k], ["kucuk harf", (k) => k.toLowerCase()], ["gorunmeyen bosluk", (k) => k + GB], ["ic bosluk", (k) => k.replace("-", "- ")]]; export const EKIP = ["İsmet", "İlknur", "Irmak", "Ilgaz", "Sedef"]; export function sayfa() { const r = uretec(TOHUM), H = {}; H.A1 = "abone"; H.C1 = "tuketim"; H.P1 = "ziyaret kodu"; H.Q1 = "teknisyen"; for (let i = 0; i < SATIR; i++) { const s = i + 2; H["A" + s] = "AB-" + String(1041 + i * 7).padStart(4, "0"); r(); // 01'deki bolge sutunu: uretec ayni sirada calisir H["C" + s] = Math.round((2 + r() * r() * 78) * 10) / 10; } // Ziyaret tablosu: ZIYARET ayri abone, her bozulma turu sirayla uygulanir. const z = uretec(TOHUM + 2); const sira = Array.from({ length: SATIR }, (_, i) => i); for (let i = SATIR - 1; i > 0; i--) { const j = Math.floor(z() * (i + 1)); [sira[i], sira[j]] = [sira[j], sira[i]]; } for (let i = 0; i < ZIYARET; i++) { const s = i + 2, k = H["A" + (2 + sira[i])]; H["P" + s] = BOZ[i % BOZ.length][1](k); // Yarisi kucuk harfle girilmis; kucuk harfe cevirme Turkce kurala gore yapilir. const t = EKIP[Math.floor(z() * EKIP.length)]; H["Q" + s] = z() < 0.5 ? t.replace(/İ/g, "i").replace(/I/g, "ı").toLowerCase() : t; } return H; } // Bozulmanin turu sayfada yazmaz; ancak inceleyerek siniflandirilir. export const tur = (k) => k !== k.replace(/^ +| +$/g, "") ? "kirpilir bosluk" : k.includes(GB) ? "gorunmeyen bosluk" : k.includes("- ") ? "ic bosluk" : k !== k.toUpperCase() ? "kucuk harf" : "temiz"; if (import.meta.url === `file://${process.argv[1]}`) { const H = sayfa(); const gor = (s) => String(s).replace(new RegExp(GB, "g"), "~").replace(/ /g, "_"); const g = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const y = g([8, 14, 8, 20]); console.log(y(["hucre", "ziyaret kodu", "uzunluk", "bozulma"])); for (const s of [2, 3, 4, 5, 6, 7]) console.log(y(["P" + s, gor(H["P" + s]), deger({ ...H, X1: "=uzunluk(P" + s + ")" }, "X1"), tur(H["P" + s])])); const P = Array.from({ length: ZIYARET }, (_, i) => H["P" + (i + 2)]); const K = new Set(Array.from({ length: SATIR }, (_, i) => H["A" + (i + 2)])); const sayim = {}; for (const k of P) sayim[tur(k)] = (sayim[tur(k)] ?? 0) + 1; console.log(`\n${ZIYARET} ziyaret kaydi — ` + Object.entries(sayim).map(([a, n]) => `${a} ${n}`).join(", ")); console.log(`Kodu abone tablosunda oldugu gibi bulunan kayit: ${P.filter((k) => K.has(k)).length}`); }
hucre ziyaret kodu uzunluk bozulma P2 AB-1286 7 temiz P3 AB-1083_ 8 kirpilir bosluk P4 _AB-1167 8 kirpilir bosluk P5 ab-1090 7 kucuk harf P6 AB-1097~ 8 gorunmeyen bosluk P7 AB-_1048 8 ic bosluk 48 ziyaret kaydi — temiz 8, kirpilir bosluk 16, kucuk harf 8, gorunmeyen bosluk 8, ic bosluk 8 Kodu abone tablosunda oldugu gibi bulunan kayit: 8
Uzunluk sütunu dersin tek görünür kanıtıdır. P3 ile P6 sekiz karakterdir ve ikisinde de
sekizinci karakter bir boşluktur; ama biri kırpılır, öteki kırpılmaz. Sayfada ikisi de yedi
karakterlik bir kod gibi görünür. Bir metin hücresinin gösterdiği şey, içerdiği şey
değildir.
Ham hâlinde 48 kaydın yalnız 8’i abone listesinde bulunuyor. Kalan 40 kayıt kaybolmuş abonelere ait değil; hepsi listede duran abonelere ait ve hiçbiri eşleşmiyor.
Dört Temizleme Kararı
Temizleme tek bir işlem değil, sıralı kararlardır ve her karar bir grup kaydı geri getirir.
Aşağıdaki blok dördünü yardımcı bir R sütununda uygular ve her aşamada eşleşen kayıt sayısıyla
rapora girecek tüketimi basar.
// eslesme.mjs — dort temizleme karari, dort eslesme sayisi ve dort rapor edilen tuketim. import { sayfa, deger, kaydir, SATIR, ZIYARET } from "./metin.mjs"; const KARAR = [ ["ham", "=P2"], ["kirpma", "=kirp(P2)"], ["kirpma + buyuk harf", "=buyuk(kirp(P2))"], ["her bosluk + buyuk harf", "=buyuk(temizle(P2))"], ["yeniden kurma", '=birlestir("AB-",sagdan(temizle(P2),4))'], ]; const H = sayfa(); const TUKETIM = new Map(Array.from({ length: SATIR }, (_, i) => [H["A" + (i + 2)], H["C" + (i + 2)]])); const anahtar = (f) => { const K = { ...H }; for (let i = 0; i < ZIYARET; i++) K["R" + (i + 2)] = kaydir(f, 0, i); return Array.from({ length: ZIYARET }, (_, i) => deger(K, "R" + (i + 2))); }; const g = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const y = g([26, 10, 12, 14, 12]); console.log(y(["R sutunu formulu", "eslesen", "eslesmeyen", "tuketim (m3)", "kurtarilan"])); let onceki = 0; for (const [ad, f] of KARAR) { const R = anahtar(f), e = R.filter((k) => TUKETIM.has(k)); const t = e.reduce((a, k) => a + TUKETIM.get(k), 0); console.log(y([ad, e.length, ZIYARET - e.length, t.toFixed(1), e.length - onceki])); onceki = e.length; } const tam = Array.from({ length: SATIR }, (_, i) => H["C" + (i + 2)]).reduce((a, b) => a + b, 0); console.log(`\n60 abonenin toplam tuketimi ${tam.toFixed(1)} m3; ziyaret edilen tuketim ` + `${anahtar(KARAR[3][1]).reduce((a, k) => a + (TUKETIM.get(k) ?? 0), 0).toFixed(1)} m3.`); // Yeniden kurma kodu okumaz, yeniden yazar: baska bir onekle gelen kayit da eslesir. const yad = { ...H, P2: "XY-1041" }; for (const [ad, f] of KARAR.slice(3)) { const K = { ...yad, R2: f }; console.log(`${ad}: XY-1041 -> ${deger(K, "R2")} (${TUKETIM.has(deger(K, "R2")) ? "eslesti" : "eslesmedi"})`); }
R sutunu formulu eslesen eslesmeyen tuketim (m3) kurtarilan ham 8 40 232.1 8 kirpma 24 24 527.6 16 kirpma + buyuk harf 32 16 673.7 8 her bosluk + buyuk harf 48 0 1204.8 16 yeniden kurma 48 0 1204.8 0 60 abonenin toplam tuketimi 1419.9 m3; ziyaret edilen tuketim 1204.8 m3. her bosluk + buyuk harf: XY-1041 -> XY-1041 (eslesmedi) yeniden kurma: XY-1041 -> AB-1041 (eslesti)
Rapora girecek sayı bu tablonun dördüncü sütununda ve dört değeri var: 232,1, 527,6, 673,7, 1204,8 m³. En küçüğü ile en büyüğü arasında beş kattan fazla fark var ve dördü de aynı veriden, aynı soruyla, aynı gün çıktı. “Ziyaret edilen abonelerin toplam tüketimi” cümlesi tek başına bu dört sayıdan hangisinin kastedildiğini söylemiyor.
Kırpma tek başına 16 kaydı geri getirdi ve orada durmak kolaydır: 24 eşleşme makul görünür, kalan 24 kayıt da “listede olmayan aboneler” diye açıklanabilir. Oysa o 24 kaydın hepsi listededir; 8’i görünmeyen boşluk, 8’i iç boşluk taşıyor ve ikisi de kırpmadan sağ çıkıyor. Bir temizlemenin yeterli olup olmadığı, temizlenmiş sayıya bakarak anlaşılamaz.
Beşinci satır aynı sayıyı üçüncü bir yoldan veriyor ve en tehlikelisi odur. Yeniden kurma kodu
okumaz, yeniden yazar: sondan dört karakteri alıp başına sabit bir önek ekler. Son iki satır
bunun bedelini gösteriyor. Başka bir önekle gelen XY-1041 kaydı dördüncü kararda eşleşmez,
beşinci kararda sessizce AB-1041 olur ve ilgisiz bir aboneye bağlanır. Eşleşme sayısı aynı
kalır, hata görünmez.
Harf Dönüşümü ve Kararın Yeri
Harf dönüşümü de tek bir işlem değildir. Aşağıdaki blok teknisyen adlarını iki dönüşümle katlayıp ekip listesiyle karşılaştırır, ardından temizleme kararının sayfada nerede durduğunu üç yazım üzerinde tarar.
// karar.mjs — harf donusumunun dile bagliligi, ve temizleme kararinin sayfada nerede durdugu. import { sayfa, deger, kaydir, EKIP, ZIYARET, GB } from "./metin.mjs"; const H = sayfa(); const g = (B) => (h) => h.map((v, j) => (j ? String(v).padStart(B[j]) : String(v).padEnd(B[0]))).join(""); const sutun = (f, n) => { const K = { ...H }; for (let i = 0; i < n; i++) K["S" + (i + 2)] = kaydir(f, 0, i); return Array.from({ length: n }, (_, i) => deger(K, "S" + (i + 2))); }; // Ekip listesi ayni islevle katlanir; eslesme iki katlanmis metnin esitligidir. const y = g([30, 10, 12, 22]); console.log(y(["S sutunu formulu", "eslesen", "eslesmeyen", "tutmayan ad"])); for (const [ad, f] of [["dil duyarsiz", "=buyuk(Q2)"], ["Turkce kurali", "=buyuktr(Q2)"]]) { const S = sutun(f, ZIYARET); const liste = EKIP.map((t) => deger({ X1: "=" + f.slice(1).replace("Q2", JSON.stringify(t)) }, "X1")); const e = S.filter((v) => liste.includes(v)).length; const tut = [...new Set(S.filter((v) => !liste.includes(v)))].join(" "); console.log(y([ad + " (" + f + ")", e, ZIYARET - e, tut || "-"])); } // Ayni sonucu ureten uc yazim: karar yardimci sutunda, formulde ya da hic yok. const YAZIM = [ ["yardimci sutun", 'R sutunu =buyuk(temizle(P2))', ZIYARET, true], ["formule gomulu", "eslestirme formulu icinde", 0, true], ["elle duzeltme", "P sutunu uzerine yazilmis", 0, false], ]; const P = Array.from({ length: ZIYARET }, (_, i) => H["P" + (i + 2)]); const bozuk = P.filter((k) => k !== k.replace(/^ +| +$/g, "") || k.includes(GB) || k.includes("- ") || k !== k.toUpperCase()).length; const z = g([16, 30, 8, 10, 12]); console.log("\n" + z(["yazim", "karar nerede", "hucre", "ham veri", "geri okuma"])); for (const [ad, yer, hucre, ham] of YAZIM) console.log(z([ad, yer, hucre, ham ? "duruyor" : "silindi", hucre ? bozuk : 0])); console.log(`\n${ZIYARET} ziyaret kaydinin ${bozuk} tanesi bozuk. Yardimci sutunda bu ${bozuk} ` + `karar tek tek okunur; elle duzeltmede hicbiri okunmaz ve ham kod da kalmaz.`);
S sutunu formulu eslesen eslesmeyen tutmayan ad dil duyarsiz (=buyuk(Q2)) 33 15 ISMET ILKNUR Turkce kurali (=buyuktr(Q2)) 48 0 - yazim karar nerede hucre ham veri geri okuma yardimci sutun R sutunu =buyuk(temizle(P2)) 48 duruyor 40 formule gomulu eslestirme formulu icinde 0 duruyor 0 elle duzeltme P sutunu uzerine yazilmis 0 silindi 0 48 ziyaret kaydinin 40 tanesi bozuk. Yardimci sutunda bu 40 karar tek tek okunur; elle duzeltmede hicbiri okunmaz ve ham kod da kalmaz.
Birinci tablo harf dönüşümünün bir metin işlemi değil, bir dil işlemi olduğunu yazıyor. Dil duyarsız dönüşüm 48 adın 33’ünü tutturdu; tutmayan 15’i iki ada ait. Küçük harfle girilen “ismet” ve “ilknur” adlarının baş harfi, dil duyarsız dönüşümde noktasız bir I’ya çevriliyor ve listedeki noktalı biçimle eşleşmiyor. Türkçe kuralını uygulayan dönüşümde 48 adın hepsi tutuyor. Aynı işlevin adı ikisinde de “büyük harfe çevir”dir; yaptıkları iş aynı değildir.
İkinci tablo dersin ikinci sayısını veriyor. Üç yazımın da eşleşme sonucu aynıdır: 48 kayıt eşleşir. Ayrım son üç sütundadır. Yardımcı sütun yazımında karar 48 hücrede durur; temizlenmiş kod görülebilir, ham kod da yerinde kalır ve bozuk gelen 40 kaydın her biri için “ne yapıldı” sorusu hücreye bakılarak cevaplanır. Formüle gömülü yazımda sonuç aynıdır ama temizlenmiş kod sayfada hiçbir yerde görünmez; hangi ziyaretin hangi aboneye bağlandığı yalnız formül metni okunarak tahmin edilebilir.
Üçüncü satır en pahalı olanıdır ve en yaygın olanıdır. Kodlar elle düzeltilip P sütununun
üzerine yazıldığında sayfa kusursuz görünür: bütün kodlar temizdir, bütün eşleşmeler tutar,
hiçbir yardımcı sütun yoktur. Geri okunabilen karar sayısı sıfırdır ve ham veri de artık
yoktur. İkinci kez bakan biri 40 kaydın düzeltildiğini göremez, hangi kuralın uygulandığını
bilemez ve düzeltmenin doğru olup olmadığını denetleyemez. Sonucu düzelten bir işlem, kararı
yok ediyorsa veriyi de yok eder.
Özet
- Metin hücresinin gösterdiği şey içerdiği şey değildir: aynı görünen iki kod farklı uzunlukta olabilir ve karşılaştırmada eşit çıkmaz.
- 48 ziyaret kaydının ham hâlinde 8’i eşleşti; kırpma 24’e, harf dönüşümü 32’ye, her tür boşluğun silinmesi 48’e çıkardı. Rapor edilen tüketim 232,1 ile 1204,8 m³ arasında oynadı.
- Sıradan kırpma yalnız normal boşluğu siler; görünmeyen boşluk ve iç boşluk taşıyan 16 kayıt kırpmadan sağ çıkar ve eşleşmemeye devam eder.
- Bir temizlemenin yettiği, temizlenmiş sayıya bakarak anlaşılmaz; ancak eşleşmeyen kayıtların tek tek incelenmesiyle anlaşılır.
- Harf dönüşümü dile bağlıdır: dil duyarsız dönüşüm 48 adın 15’ini tutturamadı, Türkçe kuralını uygulayan dönüşüm hepsini tutturdu.
- Aynı eşleşmeyi üreten üç yazımdan yalnız birinde 40 düzeltme kararı hücre hücre okunur; elle düzeltmede hem karar hem ham veri kaybolur.
Sonraki Adım
Eşleştirme kurulduğuna göre ziyaret kayıtlarının tarihleri de kullanılabilir hâle geldi. Bir sonraki adım basit görünen bir soruyu soruyor: bir arıza kaydının açılışıyla kapanışı arasında kaç gün geçti. Tarih hücrede bir sayıdır ve gösterilen biçim o sayının kendisi değildir; iki tarihin farkı bir çıkarma işlemidir, ama uçların dahil edilip edilmeyeceği bir karardır. Sonraki ders aynı kayıt kümesinin ortalama çözüm süresini iki uç kararıyla hesaplar, bir ay sonrasının ay sonunda ne anlama geldiğini ölçer ve metin olarak yazılmış tarihlerin kaçının iki farklı güne okunabildiğini sayar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.