İçeriğe geç
academia.sh

Ders 09 / 19

Sorgu İşleçleri

Karşılaştırma, mantıksal ve dizi işleçlerinin semantiği kendi değerlendiricisinde kurulur: bir alan yolunun dizinin içine inmesi, koşulların belge düzeyinde mi öge düzeyinde mi karşılandığı, olumsuzlamanın dizide ve eksik alanda ne yaptığı, her biri dönen belge sayısıyla ölçülür.

İçindekiler

Önceki ders şemayı kurdu ve gömme kararının bedelini saydı; şube sorgusu ise elle yazılmış bir koşul işleviyle çalıştırıldı. Depo bu işi kendisi yapar ve bir işleç kümesiyle yapar: karşılaştırma işleçleri, mantıksal bağlaçlar ve dizilere özgü işleçler. Bu ders o kümenin söz dizimini ezberletmez — söz dizimi depodan depoya değişir ve zaten değişir. Değişmeyen şey semantiktir ve semantik burada kendi değerlendiricisinde kurulup ölçülür.

Ölçülecek asıl soru önceki dersin sonunda ortaya çıktı. Gömülü kopya dizisine iki koşul birden sorulduğunda koşulların aynı kopyada mı, yoksa belgenin herhangi iki kopyasında mı karşılanması gerektiği belirsizdir. İki okuma da tutarlıdır, ikisi de gerçekten kullanılır ve ikisi farklı sayıda belge döndürür.

Yolun Dizinin İçine İnmesi

İlişkisel bir koşul tek bir hücreye bakar. Belge modelinde bir alan yolu birden çok değere ulaşabilir: kopya.sube yolu, üç kopyası olan bir kitapta üç değer üretir. Değerlendirici bu durumda varoluşsal davranır — koşul, ulaşılan değerlerden herhangi biri sağladığında doğar. Bu tek kural, dersin bütün ölçümlerinin kaynağıdır.

Değerlendirici üç işleç sınıfı taşır. Karşılaştırma işleçleri (=, <>, >, >=, <, <=, icinde) bir değeri bir sabitle kıyaslar. Mantıksal bağlaçlar (ve, veya, degil) koşulları birleştirir. Dizi işleçleri (ogede, tumu, hicbiri) ise ötekilerden ayrılır: değerlere değil, ögelere bakar ve alt koşulu her ögeye ayrı ayrı uygular.

// islecler.mjs — kosul degerlendirici. Yol dizilerin icine iner: bir alan yolu birden
// cok degere ulasabilir ve kosul o degerlerden herhangi biri saglarsa dogar.
const KIYAS = {
  "=": (a, b) => a === b,
  "<>": (a, b) => a !== b,
  ">": (a, b) => a > b,
  ">=": (a, b) => a >= b,
  "<": (a, b) => a < b,
  "<=": (a, b) => a <= b,
  icinde: (a, b) => b.includes(a),
};

// Yoldaki degerler. Dizi hem kendisi hem ogeleri olarak aday olur.
export function degerler(kok, yol) {
  let kume = [kok];
  for (const ad of yol.split(".")) {
    const sonraki = [];
    for (const d of kume)
      for (const o of Array.isArray(d) ? d : [d])
        if (o && typeof o === "object" && !Array.isArray(o) && ad in o) sonraki.push(o[ad]);
    kume = sonraki;
  }
  return kume.flatMap((v) => (Array.isArray(v) ? [v, ...v] : [v]));
}

// Yoldaki dizilerin kendisi: oge duzeyi islecleri bunun uzerinde calisir.
const diziler = (kok, yol) => degerler(kok, yol).filter(Array.isArray);

export function eslesir(belge, kosul, s = { kiyas: 0, oge: 0 }) {
  for (const [ad, beklenen] of Object.entries(kosul)) {
    if (ad === "ve") { if (beklenen.every((k) => eslesir(belge, k, s))) continue; return false; }
    if (ad === "veya") { if (beklenen.some((k) => eslesir(belge, k, s))) continue; return false; }
    if (ad === "degil") { if (eslesir(belge, beklenen, s)) return false; continue; }

    const sade = beklenen === null || typeof beklenen !== "object" || Array.isArray(beklenen);
    const kosullar = sade ? { "=": beklenen } : beklenen;
    for (const [islec, operand] of Object.entries(kosullar)) {
      if (islec === "var") {
        if (degerler(belge, ad).length > 0 !== operand) return false;
        continue;
      }
      if (islec === "ogede" || islec === "tumu" || islec === "hicbiri") {
        const oge = diziler(belge, ad).flat();
        s.oge += oge.length;
        const say = oge.filter((o) => eslesir(o, operand, s)).length;
        const dogru = islec === "ogede" ? say > 0
          : islec === "tumu" ? say === oge.length && oge.length > 0 : say === 0;
        if (dogru) continue;
        return false;
      }
      const aday = degerler(belge, ad);
      s.kiyas += aday.length;
      if (aday.some((d) => KIYAS[islec](d, operand))) continue;
      return false;
    }
  }
  return true;
}

export const suz = (kume, kosul, s) => kume.filter((b) => eslesir(b, kosul, s));

Aynı Soru, İki Semantik

NS7 (varsayım): katalog 20.000 kitap belgesidir, her kitabın 1–5 kopyası ve 2–4 etiketi vardır, tohum 424242’dir. NS12 (varsayım): esnek şemanın sonucu olarak belgelerin kırkta birinde yayin_yili alanı hiç yoktur. Sorular önce belge düzeyinde, sonra öge düzeyinde sorulur; her koşumda dönen belge sayısı, yapılan karşılaştırma sayısı ve incelenen öge sayısı yazılır.

// islec-olcum.mjs — ayni uc soru iki islec bicimiyle sorulur: belge duzeyinde baglac ve
// oge duzeyinde eslestirme. Donen belge sayisi ile yapilan karsilastirma sayilir.
// Ayni dizinde islecler.mjs bulunur.
import { suz } from "./islecler.mjs";

let cekirdek = 424242;                                   // gorunur tohum
const rast = () => (cekirdek = (cekirdek * 1103515245 + 12345) % 2147483648) / 2147483648;
const SUBE = ["Merkez", "Bahcelievler", "Kadikoy", "Beyoglu", "Konak", "Nilufer"];
const DURUM = ["rafta", "oduncte", "onarimda"];
const ETIKETLER = ["roman", "tarih", "cocuk", "siir", "bilim", "basvuru"];

const KATALOG = [];
for (let i = 1; i <= 20000; i += 1) {
  const kopya = [], etiket = [];
  for (let j = 0, n = 1 + Math.floor(rast() * 5); j < n; j += 1)
    kopya.push({ barkod: `B${String(i * 10 + j).padStart(7, "0")}`,
      sube: SUBE[Math.floor(rast() * 6)], durum: DURUM[Math.floor(rast() * 3)] });
  for (let j = 0, n = 2 + Math.floor(rast() * 3); j < n; j += 1)
    etiket.push(ETIKETLER[Math.floor(rast() * 6)]);
  const b = { _k: `K-${String(i).padStart(5, "0")}`, yazar: `Yazar ${i % 4000}`,
    yayin_yili: 1950 + (i % 75), etiket, kopya };
  if (i % 40 === 0) delete b.yayin_yili;                 // esnek sema: alan eksik olabilir
  KATALOG.push(b);
}

function calistir(ad, kosul) {
  const s = { kiyas: 0, oge: 0 };
  const sonuc = suz(KATALOG, kosul, s);
  console.log(`${ad.padEnd(34)} ${String(sonuc.length).padStart(5)} belge  ` +
    `karsilastirma ${String(s.kiyas).padStart(6)}  incelenen oge ${String(s.oge).padStart(6)}`);
  return sonuc.length;
}

console.log(`katalog ${KATALOG.length} belge`);
const a1 = calistir("belge duzeyi baglac", { "kopya.sube": "Kadikoy", "kopya.durum": "onarimda" });
const a2 = calistir("oge duzeyi eslestirme", { kopya: { ogede: { sube: "Kadikoy", durum: "onarimda" } } });
console.log(`  aradaki ${a1 - a2} belgede kosullar ayri kopyalarda saglaniyor`);

const b1 = calistir("kopya.durum <> oduncte", { "kopya.durum": { "<>": "oduncte" } });
const b2 = calistir("hicbir kopya oduncte degil", { kopya: { hicbiri: { durum: "oduncte" } } });
console.log(`  aradaki ${b1 - b2} belgede en az bir kopya odunctedir`);

const c1 = calistir("etiket icinde [tarih, bilim]", { etiket: { icinde: ["tarih", "bilim"] } });
const c2 = calistir("hem tarih hem bilim", { ve: [{ etiket: "tarih" }, { etiket: "bilim" }] });
const c3 = calistir("butun kopyalar rafta", { kopya: { tumu: { durum: "rafta" } } });
console.log(`  icinde ile iki kosullu baglac arasindaki fark ${c1 - c2} belge`);

calistir("yayin_yili alani yok", { yayin_yili: { var: false } });
calistir("yayin_yili >= 2010", { yayin_yili: { ">=": 2010 } });
calistir("yayin_yili < 2010 degil", { degil: { yayin_yili: { "<": 2010 } } });
katalog 20000 belge
belge duzeyi baglac                 6033 belge  karsilastirma  88749  incelenen oge      0
oge duzeyi eslestirme               3340 belge  karsilastirma  70018  incelenen oge  59494
  aradaki 2693 belgede kosullar ayri kopyalarda saglaniyor
kopya.durum <> oduncte             17787 belge  karsilastirma  59494  incelenen oge      0
hicbir kopya oduncte degil          7111 belge  karsilastirma  59494  incelenen oge  59494
  aradaki 10676 belgede en az bir kopya odunctedir
etiket icinde [tarih, bilim]       14033 belge  karsilastirma  79730  incelenen oge      0
hem tarih hem bilim                 2777 belge  karsilastirma 115287  incelenen oge      0
butun kopyalar rafta                1919 belge  karsilastirma  59494  incelenen oge  59494
  icinde ile iki kosullu baglac arasindaki fark 11256 belge
yayin_yili alani yok                 500 belge  karsilastirma      0  incelenen oge      0
yayin_yili >= 2010                  3891 belge  karsilastirma  19500  incelenen oge      0
yayin_yili < 2010 degil             4391 belge  karsilastirma  19500  incelenen oge      0

Sayıların Söylediği

İlk çift dersin asıl bulgusudur. “Kadıköy şubesinde onarımda kopyası olan kitap” sorusu belge düzeyinde sorulduğunda 6.033 belge döner, öge düzeyinde sorulduğunda 3.340. Aradaki 2.693 belgede Kadıköy’de bir kopya vardır ve onarımda bir kopya vardır, ama bunlar farklı kopyalardır — Kadıköy’deki raftadır, onarımdaki başka şubededir. Belge düzeyi bağlaç yanlış değildir; sorusu farklıdır. Yanlış olan, öge düzeyinde bir soruyu belge düzeyinde sormaktır ve sonuç %80,6 fazla belgedir. Ölçüm bu farkı sessizce üretir: iki sorgu da çalışır, ikisi de hata vermez.

İkinci çift olumsuzlamanın dizideki davranışını gösterir. kopya.durum <> "oduncte" koşulu 17.787 belge döndürür; buradaki niyet neredeyse her zaman “hiçbir kopyası ödünçte olmayan kitap”tır ve o soruya doğru yanıt 7.111’dir. Fark 10.676 belgedir — bu kitapların en az bir kopyası ödünçtedir, ama en az bir kopyası da ödünçte değildir ve varoluşsal kural koşulu doğurur. Olumsuzlama dizide, sezginin tersine, kapsayıcı çalışır: “değildir” ifadesi “hiçbiri değildir” anlamına gelmez. Doğru ifade hicbiri işlecidir ve bu, öge düzeyinde çalıştığı için 59.494 ögenin tamamını inceler.

Üçüncü çift, aynı yanlışın ters yönünü gösterir. icinde işleci diziye uygulandığında “listedeki değerlerden herhangi biri” demektir ve 14.033 belge döndürür; “hem tarih hem bilim etiketli kitap” sorusu ise iki koşullu bir bağlaçtır ve 2.777 belge döndürür. Fark 11.256’dır. Burada belge düzeyi bağlaç doğru araçtır, çünkü etiketler ayrı ögelerdedir ve koşulların aynı ögede karşılanması istenmez. İki ölçüm birlikte kuralı verir: bağlacın belge düzeyinde mi öge düzeyinde mi kurulacağını sorunun kendisi belirler, dizinin varlığı değil.

tumu işleci 1.919 belge döndürür: bütün kopyaları rafta olan kitaplar. Bu işlecin değerlendiricideki tanımı bir ayrıntı taşır — say === oge.length && oge.length > 0. İkinci koşul olmasaydı, hiç kopyası olmayan bir kitap “bütün kopyaları rafta” sorusuna doğru yanıt verirdi, çünkü boş bir küme üzerinde her önerme doğrudur. Boş dizinin bu davranışı, belge modelinde alanın hiç bulunmamasıyla birlikte iki ayrı boşluk yaratır ve ikisi de sorgunun yanıtını sessizce değiştirir.

Son üç satır esnek şemanın sorgu tarafındaki izidir. 500 belgede yayin_yili alanı yoktur. yayin_yili >= 2010 bu belgeleri döndürmez (3.891), ama degil ile sarılmış aynı koşul döndürür (4.391): eksik alan üzerinde hiçbir karşılaştırma doğmaz, dolayısıyla olumsuzlaması doğar. Fark tam olarak 500’dür — alanı olmayan belge sayısı. Bir raporun “2010 öncesi olmayan kitaplar” satırı, alanı hiç bulunmayan 500 kitabı da içine alır.

Karşılaştırma sayıları maliyeti verir. Öge düzeyi eşleştirme 70.018 karşılaştırma ve 59.494 öge incelemesi yapar, belge düzeyi bağlaç 88.749 karşılaştırma yapar. İkisi de derlemin tamamını okur: 20.000 belge. Bu, işleç seçiminin okunan belge sayısını değiştirmediği anlamına gelir — o sayıyı değiştiren şey dizindir ve bu konunun beşinci dersinin konusudur.

Özet

  • Bir alan yolu dizinin içine iner ve birden çok değere ulaşır; koşul, ulaşılan değerlerden herhangi biri sağladığında doğar.
  • “Kadıköy’de onarımdaki kopya” sorusu belge düzeyinde 6.033, öge düzeyinde 3.340 belge döndürür; 2.693 belgede iki koşul ayrı kopyalarda karşılanır.
  • Dizide olumsuzlama kapsayıcıdır: kopya.durum <> "oduncte" 17.787 belge döndürür, “hiçbir kopyası ödünçte değil” 7.111 belge; fark 10.676’dır.
  • icinde işleci diziye uygulandığında “herhangi biri” demektir (14.033 belge); “hepsi” sorusu iki koşullu belge düzeyi bağlaçla kurulur (2.777 belge).
  • Eksik alan hiçbir karşılaştırmayı doğurmaz, bu yüzden olumsuzlaması doğar: yayin_yili alanı olmayan 500 belge, koşulun olumsuzlanmış biçimine düşer.

Sonraki Adım

İşleçler bir soruyu süzmeye yetiyor, ama kütüphanenin sorularının çoğu süzmeyle bitmez: “hangi şubede kaç kopya onarımda”, “en çok ödünç alınan beş kitap”, “yazar başına ortalama kopya sayısı”. Bunların hepsi süzmenin ardından gruplama, sayma ve sıralama ister ve bunları tek bir koşul ifadesiyle söylemek mümkün değildir. Belge modeli bu işi aşamalardan oluşan bir boru hattıyla yapar: her aşama bir kayıt akışı alır, dönüştürür ve bir sonrakine verir. Aşamaların sırası akışın uzunluğunu belirler — süzme başta mı sonda mı olduğuna göre sonraki aşamaların işlediği kayıt sayısı değişir. Sonraki ders o boru hattını kurar ve sıra kararının işlenen kayıt sayısına etkisini sayar.

İ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