İçeriğe geç
academia.sh

Ders 05 / 25

Koşullu ve Listeli Oluşturma

Değişken yapılı görünümler; ögeyi üretmemek ile gizlemek arasındaki fark, liste güncellemesinde sıraya göre ve anahtara göre eşlemenin maliyeti, anahtarın seçim ölçütleri ve kimliğin korunması.

İçindekiler

Önceki dersin şablonu her durumda aynı yapıyı üretti: aynı ögeler, aynı sırada. Gerçek bir pano ise değişken yapılıdır. Süzgeç hiçbir ölçümle eşleşmediğinde tablo yerine bir ileti görünür, ölçüm listesi büyür ve küçülür, satırlar yeniden sıralanır.

Bu ders şablona koşul ve yineleme ekler. İkinci yarısı, bildirimsel modelin en sık atlanan sorusudur: yeni tanımdaki hangi satırın ağaçtaki hangi düğüme karşılık geldiğini ne belirler?

Koşullu Oluşturma

Koşullu oluşturmanın iki biçimi vardır. Koşul sağlanmadığında ya hiçbir şey üretilmez, ya da başka bir dal üretilir. İkincisinin özel bir hâli boş durumdur: liste boşken listenin yerine bir açıklama üretilir.

Buradaki karar, ögeyi üretmemek ile üretip gizlemek arasındadır ve ikisi eş değildir.

Üretilmeyen öge belge ağacında yoktur: erişilebilirlik ağacında görünmez, içindeki form alanları gönderilmez, içindeki görseller indirilmez, sekme sırasında yer almaz. Biçemle gizlenen öge ağaçta durur; gizleme yöntemine göre erişilebilirlik ağacından çıkabilir de çıkmayabilir de, ama düğümleri ve dinleyicileri bellekte kalmaya devam eder.

Ölçüt kullanım sıklığıdır. Sık açılıp kapanan ve kurulumu pahalı olan bir parça — sekmelerden biri, ölçüm grafiği — üretilip gizlenir; böylece her açılışta yeniden kurulmaz. Nadiren görünen ve büyük bir parça üretilmez; sayfanın ilk yükünü küçültmek kazançtır.

Koşulun kendisi de bir kimlik sorusu doğurur. İki dal aynı konumda aynı tür ögeyi üretiyorsa, çerçeve bunu “aynı öge, içeriği değişti” diye okur ve düğümü yeniden kullanır. Bu, dersin sonunda ölçülen soruna yol açar.

Listeli Oluşturma ve Eşleştirme Sorunu

Listeli oluşturma, bir veri dizisinin her kaydı için bir görünüm tanımı üretmektir. Üretmek kolaydır; sorun ikinci oluşturmada başlar. Elde iki liste vardır — ağaçtaki eski düğümler ve yeni tanımdaki kayıtlar — ve hangisinin hangisiyle eşleşeceğine karar verilmelidir.

İki eşleme kuralı vardır. Sıraya göre eşleme n. düğümü n. kayda bağlar. Anahtara göre eşleme, her kaydın taşıdığı bir anahtarla düğümü bulur.

// liste-eslemesi.mjs — siraya gore ve anahtara gore eslemenin urettigi islem sayisi
const ESKI = ["sicaklik", "nem", "ruzgar", "kar"];
const DURUMLAR = {
  "sona ekleme":   [...ESKI, "basinc"],
  "basa ekleme":   ["basinc", ...ESKI],
  "ortadan silme": ESKI.filter((a) => a !== "ruzgar"),
  "yeniden sirala": ["kar", "sicaklik", "ruzgar", "nem"],
};

// A) Siraya gore esleme: n. eski dugum n. yeni kayitla eslesir.
function siraylaEsle(eski, yeni) {
  const ortak = Math.min(eski.length, yeni.length);
  let icerikYaz = 0;
  for (let i = 0; i < ortak; i++) if (eski[i] !== yeni[i]) icerikYaz++;
  return { olustur: Math.max(0, yeni.length - eski.length),
    kaldir: Math.max(0, eski.length - yeni.length), tasi: 0, icerikYaz };
}

// Eski siralamasi artan kalan en uzun alt dizi: bu dugumler yerinde kalabilir.
function enUzunArtanAltDizi(dizi) {
  const kuyruk = [];
  for (const deger of dizi) {
    let alt = 0, ust = kuyruk.length;
    while (alt < ust) {
      const orta = (alt + ust) >> 1;
      if (kuyruk[orta] < deger) alt = orta + 1; else ust = orta;
    }
    kuyruk[alt] = deger;
  }
  return kuyruk.length;
}

// B) Anahtara gore esleme: dugum, kaydin anahtariyla bulunur.
function anahtarlaEsle(eski, yeni) {
  const eskiSira = new Map(eski.map((a, i) => [a, i]));
  const kalanlar = yeni.filter((a) => eskiSira.has(a)).map((a) => eskiSira.get(a));
  return {
    olustur: yeni.filter((a) => !eskiSira.has(a)).length,
    kaldir: eski.filter((a) => !yeni.includes(a)).length,
    tasi: kalanlar.length - enUzunArtanAltDizi(kalanlar),
    icerikYaz: 0,
  };
}

const satir = (etiket, s) => `${etiket.padEnd(16)} olustur ${s.olustur}  kaldir ` +
  `${s.kaldir}  tasi ${s.tasi}  icerikYaz ${s.icerikYaz}  → toplam ` +
  `${s.olustur + s.kaldir + s.tasi + s.icerikYaz}`;

console.log(`eski liste: ${ESKI.join(", ")}\n`);
for (const [ad, yeni] of Object.entries(DURUMLAR)) {
  console.log(`${ad}: ${yeni.join(", ")}`);
  console.log(`  ${satir("siraya gore", siraylaEsle(ESKI, yeni))}`);
  console.log(`  ${satir("anahtara gore", anahtarlaEsle(ESKI, yeni))}`);
}
eski liste: sicaklik, nem, ruzgar, kar

sona ekleme: sicaklik, nem, ruzgar, kar, basinc
  siraya gore      olustur 1  kaldir 0  tasi 0  icerikYaz 0  → toplam 1
  anahtara gore    olustur 1  kaldir 0  tasi 0  icerikYaz 0  → toplam 1
basa ekleme: basinc, sicaklik, nem, ruzgar, kar
  siraya gore      olustur 1  kaldir 0  tasi 0  icerikYaz 4  → toplam 5
  anahtara gore    olustur 1  kaldir 0  tasi 0  icerikYaz 0  → toplam 1
ortadan silme: sicaklik, nem, kar
  siraya gore      olustur 0  kaldir 1  tasi 0  icerikYaz 1  → toplam 2
  anahtara gore    olustur 0  kaldir 1  tasi 0  icerikYaz 0  → toplam 1
yeniden sirala: kar, sicaklik, ruzgar, nem
  siraya gore      olustur 0  kaldir 0  tasi 0  icerikYaz 3  → toplam 3
  anahtara gore    olustur 0  kaldir 0  tasi 2  icerikYaz 0  → toplam 2

Sona eklemede iki kural aynı sonucu verir: eski düğümlerin hiçbiri kaymamıştır.

Başa eklemede fark açılır. Sıraya göre eşleme dört düğümün de içeriğini yeniden yazar, çünkü her düğüm bir sonraki kayıtla eşleşmiştir; anahtara göre eşleme tek bir düğüm üretir ve hiçbirine dokunmaz. Fark listenin uzunluğuyla büyür: yüz kayıtlık bir listenin başına bir satır eklemek, sıraya göre eşlemede yüz içerik yazımı üretir.

Ortadan silmede sıraya göre eşleme son düğümü kaldırır ve kalan düğümlerden birini yeniden yazar; anahtara göre eşleme doğru düğümü kaldırır. Görünen sonuç aynıdır, ama kaldırılan düğüm farklıdır — bu ayrımın önemi bir sonraki bölümdedir.

Yeniden sıralamada anahtara göre eşleme taşımaya döner. Taşınacak en az düğüm sayısı, eski sıralaması artan kalan en uzun alt dizinin dışında kalan düğümlerin sayısıdır; dört düğümlük listede bu sayı ikidir.

Anahtarın Seçimi

Anahtarın üç koşulu vardır.

Kardeşleri arasında benzersiz. Anahtar bütün sayfada değil, yalnızca aynı listedeki kardeşler arasında ayırt edici olmalıdır. Yinelenen anahtar, iki kaydın aynı düğümü istemesi demektir.

Kararlı. Aynı kayıt, iki oluşturma arasında aynı anahtarı taşımalıdır. Oluşturma sırasında üretilen rastgele bir değer ya da sıra numarası bu koşulu bozar: her oluşturmada bütün anahtarlar değişir, bütün düğümler yeniden kurulur ve anahtar kullanmanın kazancı tersine döner.

Veriden gelen. Anahtar kaydın kendi kimliğidir; kaydı ayırt eden bir alan yoksa, kayıt üretilirken bir kimlik verilir. Görünen bir alanın — ölçüm adının — anahtar yapılması, o alan düzenlenebilir olduğunda kararlılığı bozar.

Dizinin anahtar olarak kullanılması bu koşulların ikincisini yalnızca tek bir durumda sağlar: listenin sırası hiç değişmiyor, araya ekleme ve ortadan silme olmuyorsa. Bu koşulun bugün sağlandığı bir listede yarın bir sıralama düğmesi belirdiğinde, hata listeleme kodunda değil sıralama düğmesinde görünür.

Kimliğin Korunması

Eşlemenin asıl konusu işlem sayısı değildir. Düğümün kimliği, düğüme bağlı olan ve görünüm tanımında bulunmayan her şeyin adresidir.

// kimlik-korunumu.mjs — anahtarin yerel durumu hangi kayda bagladigi
const KAYITLAR = [
  { kimlik: "s1", ad: "Sıcaklık" },
  { kimlik: "n1", ad: "Bağıl nem" },
  { kimlik: "r1", ad: "Rüzgâr" },
];

// Her satirin bir onay kutusu var; secim, satirin ornek kaydinda yasiyor.
function ciz(ornekler, kayitlar, anahtarla) {
  return kayitlar.map((kayit, dizin) => {
    const anahtar = String(anahtarla(kayit, dizin));
    if (!ornekler.has(anahtar)) ornekler.set(anahtar, { secili: false });
    return { anahtar, ad: kayit.ad, ornek: ornekler.get(anahtar) };
  });
}

const ANAHTARLAR = {
  "dizin": (kayit, dizin) => dizin,
  "kaydin kimligi": (kayit) => kayit.kimlik,
};

for (const [ad, anahtarla] of Object.entries(ANAHTARLAR)) {
  const ornekler = new Map();
  let satirlar = ciz(ornekler, KAYITLAR, anahtarla);
  satirlar.find((s) => s.ad === "Rüzgâr").ornek.secili = true;  // kullanici isaretledi

  // Listenin basina yeni bir olcum giriyor; ayni bilesenler yeniden ciziliyor.
  satirlar = ciz(ornekler, [{ kimlik: "b1", ad: "Basınç" }, ...KAYITLAR], anahtarla);
  const isaretli = satirlar.filter((s) => s.ornek.secili).map((s) => s.ad);
  console.log(`anahtar = ${ad.padEnd(15)} → isaretli satir: ${isaretli.join(", ") || "yok"}`);
}

// Kosullu olusturmada da ayni kural gecerlidir: ayni konumdaki ayni tur, ayni ornek.
function kosulluCiz(ornekler, kip, anahtarla) {
  const tanim = kip === "arama"
    ? { tur: "input", rol: "arama" }
    : { tur: "input", rol: "not" };
  const anahtar = anahtarla(tanim, kip);
  if (!ornekler.has(anahtar)) ornekler.set(anahtar, { yazilan: "" });
  return { anahtar, rol: tanim.rol, ornek: ornekler.get(anahtar) };
}

for (const [ad, anahtarla] of Object.entries({
  "yalniz tur": (tanim) => tanim.tur,
  "tur + dal": (tanim, kip) => `${tanim.tur}:${kip}`,
})) {
  const ornekler = new Map();
  kosulluCiz(ornekler, "arama", anahtarla).ornek.yazilan = "kar";  // kullanici yazdi
  const not = kosulluCiz(ornekler, "not", anahtarla);
  console.log(`\nkosul anahtari = ${ad.padEnd(11)} → dal: ${not.rol}, ` +
    `alandaki metin: ${JSON.stringify(not.ornek.yazilan)}, ` +
    `ornek sayisi: ${ornekler.size}`);
}
anahtar = dizin           → isaretli satir: Bağıl nem
anahtar = kaydin kimligi  → isaretli satir: Rüzgâr

kosul anahtari = yalniz tur  → dal: not, alandaki metin: "kar", ornek sayisi: 1

kosul anahtari = tur + dal   → dal: not, alandaki metin: "", ornek sayisi: 2

Birinci bölümde kullanıcı rüzgâr satırını işaretlemiştir. Listenin başına bir ölçüm girdiğinde dizin anahtarı işareti bir satır kaydırır ve işaret bağıl nem satırında görünür. Kaydın kimliği anahtar olduğunda işaret kaydıyla birlikte kalır.

Bu, veri hatası değildir: durumun kendisi doğrudur, yanlış olan durumun hangi kayda ait sayıldığıdır. Aynı kayma odaklı alanda, kaydırma konumunda, açılmış ayrıntı bölümünde ve süren geçişte de olur. Kullanıcı için görünen sonuç, işaretlemediği bir satırın işaretli görünmesidir.

İkinci bölüm aynı kuralın koşullu oluşturmadaki karşılığıdır. İki dal aynı konumda aynı tür ögeyi ürettiğinde eşleşme tür üzerinden kurulur, örnek yeniden kullanılır ve arama alanına yazılan metin not alanında görünmeye devam eder. Dalları ayıran bir anahtar verildiğinde iki ayrı örnek oluşur ve metin taşınmaz.

Buradan iki yönlü bir kural çıkar. Aynı kalması gereken şeyler için anahtar aynı tutulur; ayrılması gereken şeyler için anahtar bilerek değiştirilir. İkinci yön, bir bileşenin durumunu sıfırlamanın yoludur: bileşene yeni bir anahtar verilir, eski örnek sökülür, yeni örnek baştan kurulur.

Özet

  • Koşullu oluşturmada ögeyi üretmemek ile gizlemek eş değildir: üretilmeyen öge ağaçta, erişilebilirlik ağacında ve sekme sırasında yoktur; gizlenen öge bellekte kalır. Sık açılıp kapanan pahalı parçalar üretilip gizlenir, nadir görünen büyük parçalar üretilmez.
  • Sıraya göre eşleme listenin başına ekleme yapıldığında bütün düğümlerin içeriğini yeniden yazar; anahtara göre eşleme tek bir düğüm üretir.
  • Yeniden sıralamada taşınacak en az düğüm sayısı, eski sıralaması artan kalan en uzun alt dizinin dışındaki düğümlerdir.
  • Anahtar kardeşleri arasında benzersiz, oluşturmalar arasında kararlı ve veriden gelen bir değer olmalıdır; dizin yalnızca sırası hiç değişmeyen listelerde bu koşulu sağlar.
  • Kimlik korunmadığında görünüm tanımında bulunmayan durum yanlış kayda bağlanır: onay kutusu, odak, kaydırma konumu, açık ayrıntı ve süren geçiş.
  • Anahtarı bilerek değiştirmek, bir bileşenin durumunu sıfırlamanın yoludur.

Sonraki Adım

Bu ders eşleme kuralını liste düzeyinde kurdu, ama listedeki bir düğümün eşleştikten sonra ne olacağını açık bıraktı: öznitelikleri nasıl karşılaştırılır, çocukları hangi sırayla gezilir, türü değiştiğinde ne yapılır. Sonraki ders bu kuralların tamamını tek bir algoritmada toplar, sanal ağaç karşılaştırmasının hangi varsayımlarla ucuzladığını gösterir ve aynı işi karşılaştırma yapmadan çözen yaklaşımları bunun yanına koyar.

İ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