Zamonaviy tip tizimlarining ichki ishini o'rganing. Boshqaruv oqimi tahlili (CFA) xavfsizroq va mustahkamroq kod uchun qanday qilib kuchli tiplarni toraytirish usullarini ta'minlashini bilib oling.
Kompilyatorlar qanday aqlli bo'ladi: Tiplarni toraytirish va Boshqaruv oqimi tahliliga chuqur nazar
Dasturchilar sifatida biz doimo o'z vositalarimizning jim intellekti bilan o'zaro aloqada bo'lamiz. Biz kod yozamiz va bizning IDE darhol obyektda mavjud bo'lgan usullarni biladi. Biz o'zgaruvchini refaktoring qilamiz va tip tekshiruvchi biz faylni saqlashimizdan oldin ham potentsial ish vaqti xatosi haqida ogohlantiradi. Bu sehr emas; bu murakkab statik tahlil natijasidir va uning eng kuchli va foydalanuvchiga yuzlangan xususiyatlaridan biri tiplarni toraytirishdir.
Siz hech qachon string yoki number bo'lishi mumkin bo'lgan o'zgaruvchi bilan ishlaganmisiz? Ehtimol, siz operatsiyani bajarishdan oldin uning turini tekshirish uchun if iborasini yozgansiz. Ushbu blok ichida til o'zgaruvchining string ekanligini "bilar edi", bu esa stringga xos usullarni ochib beradi va sizni, masalan, raqamda .toUpperCase() ni chaqirishga urinishdan saqlaydi. Muayyan kod yo'lida tipning bunday aqlli aniqlashtirilishi tiplarni toraytirish deb ataladi.
Ammo kompilyator yoki tip tekshiruvchi bunga qanday erishadi? Asosiy mexanizm kompilyatorlar nazariyasidan olingan kuchli usul bo'lib, u Boshqaruv oqimi tahlili (CFA) deb nomlanadi. Ushbu maqola bu jarayon ortidagi pardani ochadi. Biz tiplarni toraytirish nima ekanligini, Boshqaruv oqimi tahlili qanday ishlashini o'rganamiz va konseptual amalga oshirishni ko'rib chiqamiz. Ushbu chuqur tahlil qiziquvchan dasturchi, bo'lajak kompilyator muhandisi yoki zamonaviy dasturlash tillarini shunchalik xavfsiz va samarali qiladigan murakkab mantiqni tushunishni istagan har bir kishi uchundir.
Tiplarni toraytirish nima? Amaliy tanishuv
Aslida, tiplarni toraytirish (shuningdek, tipni aniqlashtirish yoki oqim bo'yicha tiplash deb ham ataladi) - bu statik tip tekshiruvchining kodning ma'lum bir sohasida o'zgaruvchi uchun uning e'lon qilingan tipidan ko'ra aniqroq tipni aniqlash jarayonidir. U birlashma (union) kabi keng tipni oladi va mantiqiy tekshiruvlar va o'zlashtirishlar asosida uni "toraytiradi".
Keling, ba'zi umumiy misollarni ko'rib chiqaylik. TypeScript'dan uning tushunarli sintaksisi uchun foydalanamiz, garchi bu tamoyillar Python (Mypy bilan), Kotlin va boshqa ko'plab zamonaviy tillarga ham tegishli bo'lsa-da.
Toraytirishning umumiy usullari
-
`typeof` himoyalari: Bu eng klassik misoldir. Biz o'zgaruvchining primitiv turini tekshiramiz.
Misol:
function processInput(input: string | number) {
if (typeof input === 'string') {
// Bu blok ichida 'input' string ekanligi ma'lum.
console.log(input.toUpperCase()); // Bu xavfsiz!
} else {
// Bu blok ichida 'input' raqam ekanligi ma'lum.
console.log(input.toFixed(2)); // Bu ham xavfsiz!
}
} -
`instanceof` himoyalari: Obyekt turlarini ularning konstruktor funksiyasi yoki klassiga qarab toraytirish uchun ishlatiladi.
Misol:
class User { constructor(public name: string) {} }
class Guest { constructor() {} }
function greet(person: User | Guest) {
if (person instanceof User) {
// 'person' User tipiga toraytiriladi.
console.log(`Hello, ${person.name}!`);
} else {
// 'person' Guest tipiga toraytiriladi.
console.log('Hello, guest!');
}
} -
Haqiqiylikni tekshirish: `null`, `undefined`, `0`, `false` yoki bo'sh satrlarni filtrlash uchun keng tarqalgan usul.
Misol:
function printName(name: string | null | undefined) {
if (name) {
// 'name' 'string | null | undefined' dan shunchaki 'string' ga toraytiriladi.
console.log(name.length);
}
} -
Tenglik va xususiyat himoyalari: Muayyan literal qiymatlarni yoki xususiyatning mavjudligini tekshirish, ayniqsa diskriminatsiyalangan unionlar bilan, ham tiplarni toraytirishi mumkin.
Misol (Diskriminatsiyalangan Union):
interface Circle { kind: 'circle'; radius: number; }
interface Square { kind: 'square'; sideLength: number; }
type Shape = Circle | Square;
function getArea(shape: Shape) {
if (shape.kind === 'circle') {
// 'shape' Circle ga toraytiriladi.
return Math.PI * shape.radius ** 2;
} else {
// 'shape' Square ga toraytiriladi.
return shape.sideLength ** 2;
}
}
Buning foydasi juda katta. U kompilyatsiya vaqtida xavfsizlikni ta'minlaydi, ish vaqtidagi xatolarning katta bir sinfini oldini oladi. Yaxshiroq avtomatik to'ldirish bilan dasturchi tajribasini yaxshilaydi va kodni o'z-o'zini hujjatlashtiruvchi qiladi. Savol shundaki, tip tekshiruvchi bu kontekstual xabardorlikni qanday yaratadi?
Sehr ortidagi mexanizm: Boshqaruv oqimi tahlilini (CFA) tushunish
Boshqaruv oqimi tahlili - bu kompilyator yoki tip tekshiruvchiga dasturning mumkin bo'lgan bajarilish yo'llarini tushunishga imkon beruvchi statik tahlil usulidir. U kodni ishga tushirmaydi; uning strukturasini tahlil qiladi. Buning uchun ishlatiladigan asosiy ma'lumotlar strukturasi Boshqaruv oqimi grafigi (CFG)dir.
Boshqaruv oqimi grafigi (CFG) nima?
CFG - bu dasturning bajarilishi davomida o'tishi mumkin bo'lgan barcha mumkin bo'lgan yo'llarni ifodalovchi yo'naltirilgan grafikdir. U quyidagilardan iborat:
- Tugunlar (yoki Asosiy Bloklar): Boshida va oxirida istisno qilingan holda, ichkariga yoki tashqariga shoxlanishlari bo'lmagan ketma-ket bayonotlar ketma-ketligi. Bajarilish har doim blokning birinchi bayonotidan boshlanadi va to'xtamasdan yoki shoxlanmasdan oxirgisiga qadar davom etadi.
- Qirralar: Ular boshqaruv oqimini yoki asosiy bloklar o'rtasidagi "sakrashlarni" ifodalaydi. Masalan, `if` iborasi ikkita chiquvchi qirraga ega tugun yaratadi: biri "true" yo'li uchun va biri "false" yo'li uchun.
Keling, oddiy `if-else` iborasi uchun CFG ni tasavvur qilaylik:
let x: string | number = ...;
if (typeof x === 'string') { // A bloki (Shart)
console.log(x.length); // B bloki (True shox)
} else {
console.log(x + 1); // C bloki (False shox)
}
console.log('Done'); // D bloki (Birlashish nuqtasi)
Konseptual CFG taxminan shunday ko'rinishda bo'ladi:
[ Kirish ] --> [ A bloki: `typeof x === 'string'` ] --> (true qirra) --> [ B bloki ] --> [ D bloki ]
\-> (false qirra) --> [ C bloki ] --/
CFA ushbu grafikni "yurib chiqish" va har bir tugunda ma'lumotlarni kuzatishni o'z ichiga oladi. Tiplarni toraytirish uchun biz kuzatadigan ma'lumot - bu har bir o'zgaruvchi uchun mumkin bo'lgan tiplar to'plami. Qirralardagi shartlarni tahlil qilib, biz blokdan blokga o'tayotganda ushbu tip ma'lumotlarini yangilashimiz mumkin.
Tiplarni toraytirish uchun boshqaruv oqimi tahlilini amalga oshirish: Konseptual tahlil
Keling, toraytirish uchun CFA dan foydalanadigan tip tekshiruvchini qurish jarayonini bosqichma-bosqich ko'rib chiqaylik. Garchi Rust yoki C++ kabi tilda haqiqiy amalga oshirish nihoyatda murakkab bo'lsa-da, asosiy tushunchalar tushunarli.
1-qadam: Boshqaruv oqimi grafigini (CFG) qurish
Har qanday kompilyator uchun birinchi qadam manba kodini Abstrakt Sintaksis Daraxti (AST) ga ajratishdir. AST kodning sintaktik tuzilishini ifodalaydi. Keyin CFG ushbu AST dan quriladi.
CFG qurish algoritmi odatda quyidagilarni o'z ichiga oladi:
- Asosiy blok yetakchilarini aniqlash: Bayonot yetakchi (yangi asosiy blokning boshi) bo'ladi, agar u:
- Dasturdagi birinchi bayonot bo'lsa.
- Shoxlanish nishoni bo'lsa (masalan, `if` yoki `else` bloki ichidagi kod, siklning boshi).
- Shoxlanish yoki qaytarish bayonotidan keyin darhol keladigan bayonot bo'lsa.
- Bloklarni qurish: Har bir yetakchi uchun uning asosiy bloki yetakchining o'zidan va keyingi yetakchigacha bo'lgan barcha keyingi bayonotlardan iborat bo'ladi, lekin keyingi yetakchini o'z ichiga olmaydi.
- Qirralarni qo'shish: Oqimni ifodalash uchun bloklar orasida qirralar chiziladi. `if (shart)` kabi shartli bayonot shart blokidan "true" blokiga va boshqa bir "false" blokiga (yoki `else` bo'lmasa, darhol keyingi blokga) qirra yaratadi.
2-qadam: Holatlar fazosi - Tip ma'lumotlarini kuzatish
Tahlilchi CFG bo'ylab harakatlanar ekan, u har bir nuqtada "holat"ni saqlashi kerak. Tiplarni toraytirish uchun bu holat, asosan, ko'rish sohasidagi har bir o'zgaruvchini uning joriy, potentsial toraytirilgan tipi bilan bog'laydigan xarita yoki lug'atdir.
// Kodning ma'lum bir nuqtasidagi konseptual holat
interface TypeState {
[variableName: string]: Type;
}
Tahlil funksiya yoki dasturning kirish nuqtasida har bir o'zgaruvchi o'zining e'lon qilingan tipiga ega bo'lgan boshlang'ich holat bilan boshlanadi. Bizning avvalgi misolimiz uchun boshlang'ich holat: { x: String | Number } bo'lar edi. Keyin bu holat grafik bo'ylab tarqatiladi.
3-qadam: Shartli himoyalarni tahlil qilish (Asosiy mantiq)
Aynan shu yerda toraytirish sodir bo'ladi. Tahlilchi shartli shoxlanishni (`if`, `while` yoki `switch` shartini) ifodalovchi tugunga duch kelganda, u shartning o'zini tekshiradi. Shartga asoslanib, u ikkita turli xil chiqish holatini yaratadi: biri shart to'g'ri bo'lgan yo'l uchun, ikkinchisi esa shart noto'g'ri bo'lgan yo'l uchun.
Keling, typeof x === 'string' himoyasini tahlil qilaylik:
-
"True" shoxi: Tahlilchi bu naqshni taniydi. U biladiki, agar bu ifoda to'g'ri bo'lsa, `x` ning tipi `string` bo'lishi kerak. Shunday qilib, u o'z xaritasini yangilab, "true" yo'li uchun yangi holat yaratadi:
Kirish holati:
{ x: String | Number }True yo'li uchun chiqish holati:
Bu yangi, aniqroq holat keyin true shoxidagi keyingi blokga (B bloki) uzatiladi. B bloki ichida `x` ga qilingan har qanday operatsiyalar `String` tipiga nisbatan tekshiriladi.{ x: String } -
"False" shoxi: Bu ham xuddi shunday muhim. Agar
typeof x === 'string'noto'g'ri bo'lsa, bu bizga `x` haqida nima deydi? Tahlilchi "true" tipini asl tipdan ayirib tashlashi mumkin.Kirish holati:
{ x: String | Number }Olib tashlanadigan tip:
StringFalse yo'li uchun chiqish holati:
Bu aniqlashtirilgan holat "false" yo'li bo'ylab C blokiga uzatiladi. C bloki ichida `x` to'g'ri `Number` sifatida qabul qilinadi.{ x: Number }(chunki(String | Number) - String = Number)
Tahlilchida turli xil naqshlarni tushunish uchun o'rnatilgan mantiq bo'lishi kerak:
x instanceof C: True yo'lida `x` ning tipi `C` ga aylanadi. False yo'lida u o'zining asl tipida qoladi.x != null: True yo'lida `Null` va `Undefined` `x` ning tipidan olib tashlanadi.shape.kind === 'circle': Agar `shape` diskriminatsiyalangan union bo'lsa, uning tipi `kind` literal `'circle'` tipiga ega bo'lgan a'zoga toraytiriladi.
4-qadam: Boshqaruv oqimi yo'llarini birlashtirish
Bizning `if-else` iborasidan keyin D blokida bo'lgani kabi, shoxlar qayta birlashganda nima sodir bo'ladi? Tahlilchining ushbu birlashish nuqtasiga keladigan ikkita xil holati bor:
- B blokidan (true yo'li):
{ x: String } - C blokidan (false yo'li):
{ x: Number }
D blokidagi kod qaysi yo'l tanlanganidan qat'i nazar, to'g'ri bo'lishi kerak. Buni ta'minlash uchun tahlilchi bu holatlarni birlashtirishi kerak. Har bir o'zgaruvchi uchun u barcha imkoniyatlarni qamrab oladigan yangi tipni hisoblaydi. Bu odatda barcha kiruvchi yo'llardan olingan tiplarning birlashmasini (union) olish orqali amalga oshiriladi.
D bloki uchun birlashtirilgan holat: { x: Union(String, Number) } bu { x: String | Number } ga soddalashadi.
`x` ning tipi o'zining asl, kengroq tipiga qaytadi, chunki dasturning ushbu nuqtasida u har ikki shoxdan kelgan bo'lishi mumkin. Shuning uchun siz `if-else` blokidan keyin `x.toUpperCase()` dan foydalana olmaysiz — tip xavfsizligi kafolati yo'qolgan bo'ladi.
5-qadam: Sikllar va o'zlashtirishlarni qayta ishlash
-
O'zlashtirishlar: O'zgaruvchiga qiymat o'zlashtirish CFA uchun muhim hodisadir. Agar tahlilchi
x = 10;ni ko'rsa, u `x` uchun avvalgi har qanday toraytirish ma'lumotlarini bekor qilishi kerak. `x` ning tipi endi aniq o'zlashtirilgan qiymatning tipiga (`Number` bu holda) teng bo'ladi. Bu bekor qilish to'g'rilik uchun juda muhimdir. Dasturchilarning keng tarqalgan chalkashliklaridan biri bu toraytirilgan o'zgaruvchining klauzura ichida qayta o'zlashtirilishi, bu esa uning tashqarisidagi toraytirishni bekor qiladi. - Sikllar: Sikllar CFG da tsikllarni yaratadi. Siklning tahlili murakkabroq. Tahlilchi sikl tanasini qayta ishlashi, keyin sikl oxiridagi holat sikl boshidagi holatga qanday ta'sir qilishini ko'rishi kerak. U sikl tanasini bir necha marta qayta tahlil qilishi kerak bo'lishi mumkin, har safar tiplarni aniqlashtirib, tip ma'lumotlari barqarorlashguncha — bu jarayon qo'zg'almas nuqtaga erishish deb nomlanadi. Masalan, `for...of` siklida o'zgaruvchining tipi sikl ichida toraytirilishi mumkin, ammo bu toraytirish har bir iteratsiyada qayta o'rnatiladi.
Asoslardan tashqari: Ilg'or CFA tushunchalari va muammolari
Yuqoridagi oddiy model asoslarni qamrab oladi, ammo real dunyo stsenariylari sezilarli murakkablikni keltirib chiqaradi.
Tip predikatlari va foydalanuvchi tomonidan belgilangan tip himoyalari
TypeScript kabi zamonaviy tillar dasturchilarga CFA tizimiga ishoralar berishga imkon beradi. Foydalanuvchi tomonidan belgilangan tip himoyasi - bu qaytariladigan tipi maxsus tip predikati bo'lgan funksiyadir.
function isUser(obj: any): obj is User {
return obj && typeof obj.name === 'string';
}
obj is User qaytarish tipi tip tekshiruvchiga shunday deydi: "Agar bu funksiya `true` qaytarsa, siz `obj` argumenti `User` tipiga ega deb taxmin qilishingiz mumkin."
CFA if (isUser(someVar)) { ... } ga duch kelganda, u funksiyaning ichki mantig'ini tushunishi shart emas. U imzoga ishonadi. "True" yo'lida u `someVar` ni `User` ga toraytiradi. Bu sizning ilovangiz domeniga xos bo'lgan yangi toraytirish naqshlarini tahlilchiga o'rgatishning kengaytiriladigan usulidir.
Destrukturizatsiya va taxalluslarni tahlil qilish
O'zgaruvchilarga nusxalar yoki havolalar yaratganingizda nima sodir bo'ladi? CFA bu munosabatlarni kuzatib borish uchun yetarlicha aqlli bo'lishi kerak, bu taxallus tahlili sifatida tanilgan.
const { kind, radius } = shape; // shape - Circle | Square
if (kind === 'circle') {
// Bu yerda 'kind' 'circle' ga toraytiriladi.
// Ammo tahlilchi 'shape' endi Circle ekanligini biladimi?
console.log(radius); // TS da bu xato beradi! 'radius' 'shape' da mavjud bo'lmasligi mumkin.
}
Yuqoridagi misolda, mahalliy konstanta kind ni toraytirish avtomatik ravishda asl shape obyektini toraytirmaydi. Buning sababi, shape boshqa joyda qayta o'zlashtirilishi mumkin. Biroq, agar siz xususiyatni to'g'ridan-to'g'ri tekshirsangiz, u ishlaydi:
if (shape.kind === 'circle') {
// Bu ishlaydi! CFA 'shape' ning o'zi tekshirilayotganini biladi.
console.log(shape.radius);
}
Murakkab CFA nafaqat o'zgaruvchilarni, balki o'zgaruvchilarning xususiyatlarini ham kuzatishi va taxallus qachon "xavfsiz" ekanligini tushunishi kerak (masalan, agar asl obyekt `const` bo'lsa va qayta o'zlashtirilmasa).
Klauzuralar va yuqori tartibli funksiyalarning ta'siri
Funksiyalar argument sifatida uzatilganda yoki klauzuralar o'zlarining ota-ona ko'rish sohasidan o'zgaruvchilarni qamrab olganda, boshqaruv oqimi chiziqli bo'lmaydi va tahlil qilish ancha qiyinlashadi. Buni ko'rib chiqing:
function process(value: string | null) {
if (value === null) {
return;
}
// Shu nuqtada CFA 'value' ning string ekanligini biladi.
setTimeout(() => {
// Callback ichida 'value' ning tipi qanday?
console.log(value.toUpperCase()); // Bu xavfsizmi?
}, 1000);
}
Bu xavfsizmi? Bu bog'liq. Agar dasturning boshqa bir qismi `setTimeout` chaqiruvi va uning bajarilishi o'rtasida `value` ni potentsial o'zgartirishi mumkin bo'lsa, toraytirish yaroqsiz bo'ladi. Ko'pgina tip tekshiruvchilar, shu jumladan TypeScript, bu yerda konservativ yondashadilar. Ular o'zgaruvchan klauzurada qamrab olingan o'zgaruvchi o'zgarishi mumkin deb taxmin qilishadi, shuning uchun tashqi ko'rish sohasida amalga oshirilgan toraytirish, agar o'zgaruvchi `const` bo'lmasa, ko'pincha callback ichida yo'qoladi.
`never` yordamida to'liqlikni tekshirish
CFA ning eng kuchli qo'llanilishidan biri bu to'liqlikni tekshirishni ta'minlashdir. `never` tipi hech qachon sodir bo'lmasligi kerak bo'lgan qiymatni ifodalaydi. Diskriminatsiyalangan union bo'yicha `switch` iborasida, har bir holatni ko'rib chiqqaningizda, CFA ko'rib chiqilgan holatni ayirib tashlab, o'zgaruvchining tipini toraytiradi.
function getArea(shape: Shape) { // Shape - Circle | Square
switch (shape.kind) {
case 'circle':
// Bu yerda, shape - Circle
return Math.PI * shape.radius ** 2;
case 'square':
// Bu yerda, shape - Square
return shape.sideLength ** 2;
default:
// Bu yerda 'shape' ning tipi qanday?
// U (Circle | Square) - Circle - Square = never
const _exhaustiveCheck: never = shape;
return _exhaustiveCheck;
}
}
Agar siz keyinchalik `Shape` unioniga `Triangle` qo'shsangiz, lekin uning uchun `case` qo'shishni unutsangiz, `default` shoxi erishiladigan bo'ladi. Ushbu shoxdagi `shape` ning tipi `Triangle` bo'ladi. `Triangle` ni `never` tipidagi o'zgaruvchiga o'zlashtirishga urinish kompilyatsiya vaqtida xatolikka olib keladi va sizni `switch` iborangiz endi to'liq emasligi haqida darhol ogohlantiradi. Bu CFA ning to'liq bo'lmagan mantiqqa qarshi mustahkam xavfsizlik tarmog'ini ta'minlashidir.
Dasturchilar uchun amaliy oqibatlar
CFA tamoyillarini tushunish sizni yanada samaraliroq dasturchiga aylantirishi mumkin. Siz nafaqat to'g'ri, balki tip tekshiruvchi bilan "yaxshi ishlaydigan" kod yozishingiz mumkin, bu esa aniqroq kodga va tip bilan bog'liq kamroq kurashlarga olib keladi.
- Bashorat qilinadigan toraytirish uchun `const` ni afzal ko'ring: O'zgaruvchini qayta o'zlashtirish mumkin bo'lmaganda, tahlilchi uning tipi haqida kuchliroq kafolatlar bera oladi. `let` o'rniga `const` dan foydalanish murakkabroq ko'rish sohalarida, shu jumladan klauzuralarda toraytirishni saqlashga yordam beradi.
- Diskriminatsiyalangan unionlarni qabul qiling: Ma'lumotlar tuzilmalaringizni literal xususiyat (`kind` yoki `type` kabi) bilan loyihalash CFA tizimiga niyatni bildirishning eng aniq va kuchli usulidir. Ushbu unionlar bo'yicha `switch` iboralari aniq, samarali va to'liqlikni tekshirishga imkon beradi.
- Tekshiruvlarni to'g'ridan-to'g'ri saqlang: Taxallusda ko'rilganidek, obyektda xususiyatni to'g'ridan-to'g'ri tekshirish (`obj.prop`) xususiyatni mahalliy o'zgaruvchiga ko'chirib, uni tekshirishdan ko'ra toraytirish uchun ishonchliroqdir.
- CFA ni hisobga olgan holda nosozliklarni tuzating: Siz tip toraytirilishi kerak deb o'ylagan joyda tip xatosiga duch kelganingizda, boshqaruv oqimi haqida o'ylang. O'zgaruvchi biror joyda qayta o'zlashtirilganmi? U tahlilchi to'liq tushuna olmaydigan klauzura ichida ishlatilyaptimi? Bu aqliy model kuchli nosozliklarni tuzatish vositasidir.
Xulosa: Tip xavfsizligining sukutiy qo'riqchisi
Tiplarni toraytirish intuitiv, deyarli sehr kabi tuyuladi, lekin bu kompilyatorlar nazariyasidagi o'nlab yillik tadqiqotlar mahsuli bo'lib, Boshqaruv oqimi tahlili orqali hayotga tatbiq etilgan. Dasturning bajarilish yo'llari grafigini tuzish va har bir qirra bo'ylab va har bir birlashish nuqtasida tip ma'lumotlarini sinchkovlik bilan kuzatib borish orqali tip tekshiruvchilar ajoyib darajadagi aql va xavfsizlikni ta'minlaydi.
CFA - bu bizga unionlar va interfeyslar kabi moslashuvchan tiplar bilan ishlashga imkon beradigan, shu bilan birga xatolarni ishlab chiqarishga yetib bormasdan oldin ushlaydigan jim qo'riqchidir. U statik tiplashni qattiq cheklovlar to'plamidan dinamik, kontekstga sezgir yordamchiga aylantiradi. Keyingi safar muharriringiz `if` bloki ichida mukammal avtomatik to'ldirishni ta'minlaganda yoki `switch` iborasida ko'rib chiqilmagan holatni belgilaganda, siz buning sehr emasligini bilasiz — bu Boshqaruv oqimi tahlilining nafis va kuchli mantig'i ishlayotgan bo'ladi.