İçeriğe geç
academia.sh

Ders 21 / 27

Biçim ve Doğrulama Denetimleri

Altyapı kaydının uygulanmadan önce sınanması: kurgu tanım dosyaları üzerinde biçim, söz dizimi ve tip/başvuru denetimlerinin gerçekten koşturulması, on altı bilinen kusurda her denetimin tuttuğu, yalnız kendisinin tuttuğu ve kaçırdığı sayıların ölçülmesi, koşum maliyetinin okunan dosya ve karşılaştırma cinsinden sayılması, statik denetimin göremediği kusur sınıfının adıyla yazılması.

İçindekiler

Kayıt yazıldı: altyapı bir yapılandırma dilinde tanımlandı, dosyalara bölündü, tekrar eden parçalar modülleşti. Buraya kadar ölçülen kaydın düzeniydi; hiç ölçülmeyen ise doğruluğu. Bu dosyalar uygulandığında istenen altyapı mı çıkıyor, yoksa uygulama yarıda mı kalıyor? Soru uygulamadan önce sorulmazsa yanıtı uygulamanın kendisi verir ve yanıtın bedeli yaratılmış kaynaktır.

Statik denetim kaydı uygulamadan önce okur: hiçbir kaynağa dokunmaz, sağlayıcıya bağlanmaz, yalnız diskteki metni okur. Bu ders üç statik denetimi yazar ve kurgu tanım dosyaları üzerinde koşturur — biçim, söz dizimi, tip ve başvuru tutarlılığı. Her denetimin bilinen kusur kümesinde tuttuğu ve yalnız kendisinin tuttuğu, kaçırdığı ve koşum maliyeti sayılır; sonda statik denetimin göremediği kusur sınıfı uygulamada ölçülür. Through-line bölgesel ölçüm ağıdır ve kurgudur.

Kayıt ve Bilinen Kusur Kümesi

AD1 — ölçüm ağının üretim ortamı dört tanım dosyasında on bir kaynakta tutulur: üç ağ, beş makine, bir kuyruk, bir veri deposu, bir nesne deposu (kurgu). AD2 — yapılandırma dilinin biçimi kendi küçük biçimimizdir: sütun 0’da blok başlığı, altında iki boşluk girintili alanlar, = düz değer, -> başvuru. AD3 — bilinen kusur kümesi on altı kusurdur ve elle işaretlenmiştir; her kusur temiz kaydın belirli bir satırına enjekte edilir ve bulgunun hangi kusura karşılık geldiği satır numarasından okunur.

// kur.mjs — olcum aginin kaydini yazar, kopyasina bilinen kusurlari enjekte eder (kurgu)
import { mkdirSync, writeFileSync } from "node:fs";

// bicim: sutun 0'da "kaynak <tur>/<ad>:", altinda iki bosluk girintili alan satirlari;
// "=" duz deger, "->" baska bir kaynaga basvuru, "#" yorum, bloklar arasinda bir bos satir
const blok = (tur, ad, alan) => `kaynak ${tur}/${ad}:\n` +
  alan.map(([a, y, d]) => `  ${a.padEnd(8)}${y} ${d}`).join("\n") + "\n";
const MAKINE = [["bati-1", "bati", "orta", 40], ["bati-2", "bati", "orta", 40],
  ["bati-3", "bati", "buyuk", 80], ["dogu-1", "dogu", "orta", 40], ["dogu-2", "dogu", "buyuk", 80]];
const DOSYA = {
  "ag.tanim": ["bati", "dogu", "kuzey"].map((b, i) => blok("ag", `omurga-${b}`,
    [["bolge", "=", b], ["blok", "=", `10.2${i}.0.0/16`], ["ozel", "=", "evet"]])).join("\n"),
  "makine.tanim": MAKINE.map(([n, b, by, d]) => blok("makine", `toplayici-${n}`,
    [["bolge", "=", b], ["boyut", "=", by], ["disk_gb", "=", d],
      ["ag", "->", `ag/omurga-${b}`]])).join("\n"),
  "akis.tanim": blok("kuyruk", "olcum-kuyrugu", [["bolge", "=", "bati"], ["saklama", "=", 72],
    ["ag", "->", "ag/omurga-bati"]]) + "\n" + blok("veri_deposu", "olcum-deposu",
    [["bolge", "=", "bati"], ["boyut", "=", "buyuk"], ["disk_gb", "=", 500], ["yedek", "=", 14],
      ["ag", "->", "ag/omurga-bati"]]),
  "arsiv.tanim": blok("nesne_deposu", "olcum-arsiv", [["bolge", "=", "bati"],
    ["surumlu", "=", "evet"], ["sinif", "=", "soguk"]]),
};

// bilinen kusur kumesi: [kod, dosya, temiz kayittaki satir, yerine gelen, kusurun konumu]
const KUSUR = [
  ["K01", "ag.tanim", 2, "  bolge   = bati  "],
  ["K02", "ag.tanim", 9, "  ozel    = acik"],
  ["K03", "makine.tanim", 3, "   boyut   = orta"],
  ["K04", "makine.tanim", 5, "  ag      -> nesne_deposu/olcum-arsiv"],
  ["K05", "makine.tanim", 10, "  disk_gb=40"],
  ["K06", "makine.tanim", 16, "  disk_gb = seksen"],
  ["K07", "makine.tanim", 18, "@sil", 19],
  ["K08", "makine.tanim", 22, "  disk_gb = 40\n  disk_gb = 60"],
  ["K09", "makine.tanim", 23, "  ag      -> ag/omurga-guney"],
  ["K10", "makine.tanim", 25, "kaynak makine/toplayici-dogu-1:"],
  ["K11", "ag.tanim", 11, "kaynak ag/omurga-kuzey"],
  ["K12", "akis.tanim", 2, "  bolge   = bati\n  bolgeler = bati,dogu"],
  ["K13", "akis.tanim", 3, "\tsaklama = 72"],
  ["K14", "akis.tanim", 8, "  boyut   ="],
  ["K15", "akis.tanim", 11, "  # ag baglantisi sonra eklenecek", 6],
  ["K16", "arsiv.tanim", 4, "  sinif   = soguk\n  etiket olcum arsivi"],
];

mkdirSync("kayit", { recursive: true });
mkdirSync("taslak", { recursive: true });
const konum = [];
for (const [dosya, icerik] of Object.entries(DOSYA)) {
  writeFileSync(`kayit/${dosya}`, icerik);
  const yama = new Map(KUSUR.filter((k) => k[1] === dosya).map((k) => [k[2], k[3]]));
  const yeni = [], harita = {};
  icerik.split("\n").forEach((s, i) => { // bir kusur satiri silebilir ya da satir ekleyebilir
    const d = yama.get(i + 1);
    if (d === "@sil") return;
    yeni.push(...(d ?? s).split("\n"));
    harita[i + 1] = yeni.length; // temiz kayittaki satir -> taslaktaki son karsiligi
  });
  writeFileSync(`taslak/${dosya}`, yeni.join("\n"));
  for (const [kod, , satir, , k] of KUSUR.filter((x) => x[1] === dosya))
    konum.push({ kod, dosya, satir: harita[k ?? satir] });
}
writeFileSync("kusur.json", JSON.stringify(konum));

const metin = Object.values(DOSYA).join("");
console.log(`kayit: ${Object.keys(DOSYA).length} dosya, ${metin.match(/^kaynak /gm).length} kaynak, ` +
  `${metin.split("\n").length} satir; taslak: ayni kayit + ${KUSUR.length} bilinen kusur\n`);
console.log(DOSYA["akis.tanim"].split("\n").slice(0, 4).join("\n"));
kayit: 4 dosya, 11 kaynak, 59 satir; taslak: ayni kayit + 16 bilinen kusur

kaynak kuyruk/olcum-kuyrugu:
  bolge   = bati
  saklama = 72
  ag      -> ag/omurga-bati

Üç Statik Denetim

AD4 — biçim kuralları altı tanedir ve ekipçe kabul edilmiştir. AD5 — her kaynak türünün alanları, tipleri ve zorunlulukları kayıtla birlikte tutulan bir şemadadır. Üçü de aynı biçimde bulgu üretir — dosya, satır, kural — ama bağlamları farklıdır: biçim tek satıra bakar, söz dizimi satıra ve bloğuna bakar, tip/başvuru kaydın tamamını ister; çünkü bir makinenin ag alanı başka dosyadaki kaynağa işaret eder.

// denetim.mjs — ayni kayit uzerinde uc statik denetim: bicim, soz dizimi, tip/basvuru
import { readFileSync, readdirSync } from "node:fs";

const SEMA = { // tur -> "alan:tip" listesi; "!" zorunlu, "@t" t turunden bir kaynaga basvuru
  ag: "bolge:metin! blok:metin! ozel:mantik!",
  makine: "bolge:metin! boyut:metin! disk_gb:sayi! ag:@ag!",
  kuyruk: "bolge:metin! saklama:sayi! ag:@ag!",
  veri_deposu: "bolge:metin! boyut:metin! disk_gb:sayi! yedek:sayi! ag:@ag!",
  nesne_deposu: "bolge:metin! surumlu:mantik! sinif:metin!",
};
const alanlar = (tur) => Object.fromEntries(SEMA[tur].split(" ").map((x) => x.split(":")));
const BASLIK = /^kaynak ([a-z_]+)\/([a-z0-9-]+):$/, ALAN = /^\s+([a-z_]+)\s*(=|->)\s*(.*)$/;
const oku = (dz) => readdirSync(dz).filter((d) => d.endsWith(".tanim"))
  .map((d) => [d, readFileSync(`${dz}/${d}`, "utf8").split("\n")]);
const say = { bicim: 0, sozdizim: 0, tip: 0 };

// 1) bicim: alti satir-yerel kural; bir satirdan baska hicbir sey okumaz
const R = [
  ["R1", (s) => s.length && s !== s.trimEnd()],                          // satir sonunda bosluk
  ["R2", (s) => /^ *\t/.test(s)],                                        // girintide sekme
  ["R3", (s) => /^ +\S/.test(s) && !/^ {2}\S/.test(s)],                  // girinti iki bosluk degil
  ["R4", (s) => /=|->/.test(s) && !/^\s*#/.test(s) && !/ (=|->)( |$)/.test(s)],  // ayrac boslugu yok
  ["R5a", (s) => /^[^\s#]/.test(s) && !s.endsWith(":")],                 // baslik iki nokta ile bitmiyor
  ["R5b", (s, i, L) => /^[^\s#]/.test(s) && i > 0 && L[i - 1] !== ""],   // blok oncesi bos satir yok
];
function bicim(dosyalar) {
  const b = [];
  for (const [d, L] of dosyalar) L.forEach((s, i) => R.forEach(([k, f]) => {
    say.bicim += 1;
    if (f(s, i, L)) b.push({ dosya: d, satir: i + 1, kural: k });
  }));
  return b;
}

// 2) soz dizimi: satir ve blok yerel; ayni dosyanin disina hic bakmaz
function sozdizim(dosyalar) {
  const b = [], ad = new Map();
  for (const [d, L] of dosyalar) {
    let anahtar = null;
    L.forEach((s, i) => {
      const E = (k) => b.push({ dosya: d, satir: i + 1, kural: k });
      say.sozdizim += 3;
      if (s === "" || /^\s*#/.test(s)) return;
      if (/^\S/.test(s)) {
        const m = BASLIK.exec(s);
        anahtar = null;
        if (!m) return E("P1 baslik kalibi tutmuyor");
        anahtar = new Set();
        const yol = `${m[1]}/${m[2]}`;
        if (ad.has(yol)) E(`P4 yinelenen kaynak adi (${ad.get(yol)})`);
        else ad.set(yol, `${d}:${i + 1}`);
        return;
      }
      const m = ALAN.exec(s);
      if (!m) return E("P2 taninmayan satir");
      if (m[3].trim() === "") return E("P2 deger yok");
      if (anahtar?.has(m[1])) E(`P3 yinelenen anahtar (${m[1]})`);
      anahtar?.add(m[1]);
    });
  }
  return b;
}

// 3) tip/basvuru: kaydin tamamini ister, cunku basvurular dosya sinirini asar
function tipBasvuru(dosyalar) {
  const kaynak = [], b = [];
  for (const [d, L] of dosyalar) { // bozuk baslikli blok modele hic girmez
    let c = null;
    L.forEach((s, i) => {
      const m = /^\S/.test(s) ? BASLIK.exec(s) : ALAN.exec(s);
      if (/^\S/.test(s)) c = m ? { tur: m[1], ad: m[2], dosya: d, satir: i + 1, alan: {} } : null;
      else if (c && m) c.alan[m[1]] = { deger: m[3].trim(), satir: i + 1 };
      else return;
      if (/^\S/.test(s) && c) kaynak.push(c);
    });
  }
  const tipi = (v) => (["evet", "hayir"].includes(v) ? "mantik" : /^-?\d+$/.test(v) ? "sayi" : "metin");
  for (const k of kaynak) {
    const sema = alanlar(k.tur);
    for (const [a, v] of Object.entries(k.alan)) {
      const E = (kural) => b.push({ dosya: k.dosya, satir: v.satir, kural });
      say.tip += 2;
      if (!sema[a]) { E(`C1 semada yok (${a})`); continue; }
      const t = sema[a].replace("!", "");
      if (!t.startsWith("@")) {
        if (v.deger === "" || tipi(v.deger) !== t) E(`C2 ${t} degil ("${v.deger}")`);
        continue;
      }
      let h = null;
      for (const x of kaynak) { say.tip += 1; if (`${x.tur}/${x.ad}` === v.deger) { h = x; break; } }
      if (!h) E(`C4 hedef yok (${v.deger})`);
      else if (h.tur !== t.slice(1)) E(`C5 yanlis tur (${h.tur})`);
    }
    for (const [a, t] of Object.entries(sema)) {
      say.tip += 1;
      if (t.endsWith("!") && !k.alan[a])
        b.push({ dosya: k.dosya, satir: k.satir, kural: `C3 zorunlu alan yok (${a})` });
    }
  }
  return b;
}

const taslak = oku("taslak"), temiz = oku("kayit");
const D = [["bicim", bicim, 1], ["sozdizim", sozdizim, 1], ["tip/basvuru", tipBasvuru, 4]];
const bulgu = D.map(([ad, f]) => [ad, f(taslak)]);
const m = [say.bicim, say.sozdizim, say.tip]; // sayaclar yalniz taslak kosumundan okunur
const K = JSON.parse(readFileSync("kusur.json", "utf8"));
const kod = (f) => K.find((x) => x.dosya === f.dosya && x.satir === f.satir)?.kod ?? "??";

console.log(`${"denetim".padEnd(13)}${"kod".padEnd(5)}${"konum".padEnd(20)}kural`);
for (const [ad, l] of bulgu) for (const f of l)
  console.log(`${ad.padEnd(13)}${kod(f).padEnd(5)}${`${f.dosya}:${f.satir}`.padEnd(20)}${f.kural}`);

const tut = bulgu.map(([, l]) => new Set(l.map(kod)));
console.log(`\n${"denetim".padEnd(13)}${"bulgu".padStart(6)}${"tuttugu".padStart(9)}` +
  `${"yalniz kendisi".padStart(16)}${"kacirdigi".padStart(11)}${"karsilastirma".padStart(15)}` +
  `${"tek dosya icin".padStart(16)}`);
bulgu.forEach(([ad, l], j) => {
  const y = [...tut[j]].filter((k) => tut.every((s, o) => o === j || !s.has(k))).length;
  console.log(`${ad.padEnd(13)}${String(l.length).padStart(6)}${String(tut[j].size).padStart(9)}` +
    `${String(y).padStart(16)}${String(K.length - tut[j].size).padStart(11)}` +
    `${String(m[j]).padStart(15)}${String(D[j][2]).padStart(16)}`);
});
console.log(`\nuc denetim birlikte ${K.length} kusurun ` +
  `${new Set(tut.flatMap((s) => [...s])).size} tanesini tutar; hicbir kusura karsilik gelmeyen ` +
  `bulgu ${bulgu.flatMap(([, l]) => l).filter((f) => kod(f) === "??").length}; ` +
  `temiz kayitta toplam bulgu ${D.reduce((s, [, f]) => s + f(temiz).length, 0)}`);
denetim      kod  konum               kural
bicim        K01  ag.tanim:2          R1
bicim        K11  ag.tanim:11         R5a
bicim        K13  akis.tanim:4        R2
bicim        K03  makine.tanim:3      R3
bicim        K05  makine.tanim:10     R4
bicim        K07  makine.tanim:18     R5b
sozdizim     K11  ag.tanim:11         P1 baslik kalibi tutmuyor
sozdizim     K14  akis.tanim:9        P2 deger yok
sozdizim     K16  arsiv.tanim:5       P2 taninmayan satir
sozdizim     K08  makine.tanim:22     P3 yinelenen anahtar (disk_gb)
sozdizim     K10  makine.tanim:25     P4 yinelenen kaynak adi (makine.tanim:18)
tip/basvuru  K02  ag.tanim:9          C2 mantik degil ("acik")
tip/basvuru  K12  akis.tanim:3        C1 semada yok (bolgeler)
tip/basvuru  K14  akis.tanim:9        C2 metin degil ("")
tip/basvuru  K15  akis.tanim:7        C3 zorunlu alan yok (ag)
tip/basvuru  K04  makine.tanim:5      C5 yanlis tur (nesne_deposu)
tip/basvuru  K06  makine.tanim:16     C2 sayi degil ("seksen")
tip/basvuru  K09  makine.tanim:23     C4 hedef yok (ag/omurga-guney)

denetim       bulgu  tuttugu  yalniz kendisi  kacirdigi  karsilastirma  tek dosya icin
bicim             6        6               5         10            384               1
sozdizim          5        5               3         11            192               1
tip/basvuru       7        7               6          9            131               4

uc denetim birlikte 16 kusurun 16 tanesini tutar; hicbir kusura karsilik gelmeyen bulgu 0; temiz kayitta toplam bulgu 0

Kapsam, Örtüşme ve Bedel

Üç denetim on altı kusurun on altısını da tutar, ama hiçbiri tek başına yetmez: en geniş kapsamlı olan tip/başvuru bile dokuz kusuru kaçırır. Örtüşme iki yerdedir — başlıktaki iki nokta eksikliği (K11) hem biçime hem söz dizimine, boş bırakılmış değer (K14) hem söz dizimine hem tipe takılır. Kalan on dört kusur tek bir denetimin işidir; “yalnız kendisi” sütunu 5, 3 ve 6 verir.

Karşılaştırma sayıları sezginin tersini söyler: en çok karşılaştırma yapan denetim en az bağlam isteyendir. Biçim 384 karşılaştırma yapar (64 satır çarpı altı kural), tip/başvuru yalnız 131. Bedel karşılaştırmada değil kapsamdadır: tek dosya değiştiğinde ilk iki denetim o dosyayı okuyup karar verebilir, tip/başvuru dördünü birden okumak zorundadır. Üstelik başvuru çözümü kaynak listesini baştan taradığından tip denetiminin karşılaştırması kaynak sayısıyla karesel büyür. Tabloda sessiz iki sayı daha var: kusura karşılık gelmeyen bulgu ile temiz kayıttaki toplam bulgu sıfırdır.

Statik Denetimin Göremediği Sınıf

Üç denetimin de göremediği kusurların ortak adı sağlayıcı gerçeğine bağlı kusurlardır: kararı veren durum kayıtta değildir — bölgedeki kota, bölgede bulunan makine boyutları, ad havuzunda tutulmuş bir ad, o kaynak türünde yaratma izni. Hiçbiri metinde yazmaz.

AD6 — sağlayıcının kotası, bölge-boyut kataloğu, ad havuzu, izinleri ve kaynak yaratma süreleri kurgudur. Aşağıdaki blok statik denetimin sıfır bulgu verdiği temiz kaydı bu modele uygular; kalemler kaydın okunma sırasına göre işlenir ve reddedilen atlanır.

// uygulama.mjs — temiz kayit bir saglayici modeline uygulanir; saglayici da sureler de kurgudur
import { readFileSync, readdirSync } from "node:fs";

const SAGLAYICI = {
  kota: { bati: 2, dogu: 4 },                            // bolge basina makine ust siniri
  katalog: { bati: ["orta", "buyuk"], dogu: ["orta"] },  // bolgede bulunan boyutlar
  tutulanAd: ["olcum-arsiv"],                            // ad kuresel benzersiz
  izin: { ag: 1, makine: 1, kuyruk: 1, veri_deposu: 0, nesne_deposu: 1 },
};
const SURE = { ag: 20, makine: 45, kuyruk: 30, veri_deposu: 180, nesne_deposu: 15, red: 5 };

const kaynak = [];
for (const d of readdirSync("kayit")) for (const s of readFileSync(`kayit/${d}`, "utf8").split("\n")) {
  const h = /^kaynak ([a-z_]+)\/([a-z0-9-]+):$/.exec(s), a = /^\s+([a-z_]+)\s*=\s*(.+)$/.exec(s);
  if (h) kaynak.push({ tur: h[1], ad: h[2], alan: {} });
  else if (a) kaynak.at(-1).alan[a[1]] = a[2].trim();
}

let saat = 0, yaratilan = 0;
const red = [], sayilan = {};
for (const k of kaynak) {
  let hata = null;
  if (!SAGLAYICI.izin[k.tur]) hata = "yetki reddi";
  else if (SAGLAYICI.tutulanAd.includes(k.ad)) hata = "ad tutulmus";
  else if (k.tur === "makine") {
    sayilan[k.alan.bolge] = (sayilan[k.alan.bolge] ?? 0) + 1;
    if (sayilan[k.alan.bolge] > SAGLAYICI.kota[k.alan.bolge]) hata = "kota asildi";
    else if (!SAGLAYICI.katalog[k.alan.bolge].includes(k.alan.boyut)) hata = "katalogda yok";
  }
  if (hata) red.push({ ad: `${k.tur}/${k.ad}`, hata, saniye: saat, yaratilan });
  else yaratilan += 1;
  saat += hata ? SURE.red : SURE[k.tur];
}

console.log(`${kaynak.length} kalem uygulandi: ${yaratilan} yaratildi, ${red.length} reddedildi, ` +
  `toplam ${saat} saniye\n`);
console.log(`${"reddedilen kalem".padEnd(28)}${"neden".padEnd(15)}${"kacinci saniyede".padStart(17)}` +
  `${"o ana dek yaratilan".padStart(21)}`);
for (const r of red) console.log(`${r.ad.padEnd(28)}${r.hata.padEnd(15)}` +
  `${String(r.saniye).padStart(17)}${String(r.yaratilan).padStart(21)}`);
11 kalem uygulandi: 7 yaratildi, 4 reddedildi, toplam 245 saniye

reddedilen kalem            neden           kacinci saniyede  o ana dek yaratilan
veri_deposu/olcum-deposu    yetki reddi                   90                    4
nesne_deposu/olcum-arsiv    ad tutulmus                   95                    4
makine/toplayici-bati-3     kota asildi                  190                    6
makine/toplayici-dogu-2     katalogda yok                240                    7

Dört kusur da statik denetimin temiz saydığı kayıttadır; fark zamanda ve bedeldedir. Üç denetim 64 satırı 707 karşılaştırmayla tarar ve sonucu hiç kaynak yaratmadan verir; sağlayıcıya bağlı ilk kusur 90., sonuncusu 240. saniyede görünür ve o anlara kadar dört ve yedi kaynak yaratılmıştır. Koşum bittiğinde altyapı yarımdır: kuyruk vardır, beslediği veri deposu yoktur. Bu sınıfı statik denetime taşımanın tek yolu sağlayıcı durumunu kayda getirmektir — yeni bir kayıt, yeni bir sapma kaynağı.

Özet

  • Kurgu ölçüm ağının kaydı dört dosyada on bir kaynak tutar; kopyasına on altı bilinen kusur enjekte edilip üç statik denetim gerçek dosyalarda koşturuldu.
  • Biçim 6 bulgu verdi (5’i yalnız kendisinin), söz dizimi 5 (3’ü), tip/başvuru 7 (6’sı); üçü birlikte 16 kusurun 16’sını tuttu, en geniş denetim tek başına 9 kusur kaçırdı.
  • Örtüşme iki kusurdadır: iki nokta eksikliği biçim ile söz dizimine, boş değer söz dizimi ile tipe takılır.
  • Bedel karşılaştırmada değil kapsamdadır: biçim 384, söz dizimi 192, tip/başvuru 131 karşılaştırma yapar, ama tek dosya değişince ilk ikisi 1, tip/başvuru 4 dosya okur.
  • Göremediği sınıf sağlayıcı gerçeğine bağlı kusurlardır (kota, bölge kataloğu, tutulmuş ad, yetki); sıfır bulguyla geçen kayıtta dördü uygulamada çıktı (ilki 90., sonuncusu 240. saniyede) ve geride 7 yaratılmış kaynakla yarım bir altyapı bıraktı.

Sonraki Adım

Bu dersin denetimleri kaydın kendi kurallarını sınadı. Hiçbiri “bu kaynak açık ağa bakmamalı” türünden bir kuralı sınamadı; böyle kurallar dışarıdan gelir ve kaydın doğruluğuyla değil kabul edilebilirliğiyle ilgilidir. Sonraki ders bir politika kümesini aynı kayıt üzerinde koşturur: politika tanım üzerinde mi plan üzerinde mi koşmalı? İki tarama aynı ihlal kümesinde karşılaştırılır — hangisi hesaplanan değerleri görüyor ve muafiyet kaç ihlale yol açıyor.

İ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