Онлайн час в Google Meet
Лого на 91. НЕГ „Проф. Константин Гълъбов“

11. клас · Избираем модул · Урок 7

Съветник по правила на C#

Съветникът от урок 6 става програма на C#: правилата като вложени условия, после като масив от обекти, който цикъл проверява; извод с обосновка „защото…“ и проверка с гранични случаи.

От хартия към програма

Два часа бяхте машината за извод. Днес я пишете. Всичко, което ви трябва, го знаете от Модул 1: условия, методи, масиви и един прост клас. Няма нищо „изкуствено интелигентно“ в кода — и точно това е поуката: първите AI системи са били обикновени програми с добре подредено знание.

Това е първата от трите програми на C#, които се оценяват през годината. Критериите са три: работи върху дадените примери, издържа граничните случаи от урока и можете да обясните всеки ред.

Правилата като условни конструкции

Най-прекият превод на дърво в код: всеки въпрос е if, всеки отговор „да“ влиза в блока, „не“ — в else. Първо ни трябва метод, който задава въпрос и връща отговора като bool:

static bool Pitay(string vapros)
{
    Console.WriteLine(vapros + " (да/не)");
    string otgovor = Console.ReadLine();
    return otgovor == "да";
}

После дървото се преписва възел по възел. Забележете, че въпросът „Вали ли?“ се задава само ако разстоянието е под 2 км — програмата пита само за това, което ѝ трябва, точно както извеждането назад от урок 5:

if (Pitay("Разстоянието е под 2 км?"))
{
    if (Pitay("Вали ли?")) savet = "автобус";
    else                   savet = "пеша";
}
else
{
    if (Pitay("Има ли пряк автобус или метро?")) savet = "градски транспорт";
    else if (Pitay("Бързате ли?"))               savet = "такси";
    else                                         savet = "колело";
}
  • Всяко листо на дървото е едно присвояване на savet.
  • Дълбочината на дървото е дълбочината на влагането — три нива тук.
  • Нов въпрос = нов if вътре в правилния клон. Лесно за пет правила, мъчително за петдесет.

Обосновката „защото…“

Съветник, който казва само „такси“, не е по-добър от монета. Заедно със съвета пазим и обосновка — низ, който описва защо. В пример 1 тя се записва в същото листо, в което се записва и съветът:

savet = "такси";
zashto = "разстоянието е голямо, няма пряка линия и бързате";

Добрата обосновка:

  • съдържа само фактите, от които наистина зависи изводът — не всички зададени въпроси;
  • изброява ги в реда, в който са проверени — това е пътят в дървото;
  • може да се прочете от потребителя без да гледа кода.

Правилата като данни

Урок 5 обеща, че базата от знания може да е отделена от програмата. Ето как: едно правило е обект с масив от условия и извод.

class Pravilo
{
    public string[] Uslovia;   // всички трябва да са сред фактите
    public string Izvod;

    public Pravilo(string[] uslovia, string izvod)
    {
        Uslovia = uslovia;
        Izvod = izvod;
    }
}

Фактите са низове в масив: „иглички“, „жълъд“… Машината за извод е един цикъл — минава правилата по ред и спира на първото, чиито условия всички са налице:

foreach (Pravilo p in pravila)
{
    bool srabotva = true;
    foreach (string u in p.Uslovia)
        if (!Ima(u)) srabotva = false;

    if (srabotva)
    {
        Console.WriteLine("Извод: " + p.Izvod);
        Console.WriteLine("Защото: " + string.Join(" и ", p.Uslovia));
        return;
    }
}
Console.WriteLine("Не мога да преценя — нямам правило за тези факти.");

Обосновката тук идва без усилие — това са условията на сработилото правило. А Ima(u) е просто Array.IndexOf(fakti, u) >= 0.

Правила като ifПравила като данни
Ново правилонов if на правилното място, ново компилираненов ред в масива
Кой може да го добависамо програмисти специалист, ако правилата се четат от файл
Ред на въпроситевграден в дървото — пита само нужнотосъбира всички факти, после разсъждава
Обосновкапише се ръчно във всяко листоусловията на сработилото правило

Гранични случаи

Програмата от пример 1 приема за „не“ всичко, което не е точно „да“: „Да“, „ да“, „yes“, „може би“. Потребителят няма как да разбере защо съветът е грешен. Граничните случаи, които съветникът трябва да издържа:

  • главни букви и интервали — „ДА“, „Да “ — нормализирайте с Trim() и ToLower();
  • отговор извън очакваното — „може би“ — кажете, че не разбирате, и питайте пак;
  • свършил вход — ReadLine() връща null — приемете „не“ вместо да гръмнете;
  • нито едно сработило правило — кажете го честно, не връщайте последното правило;
  • две сработили правила — първото печели; проверете дали редът е този, който искате.
static bool Pitay(string vapros)
{
    while (true)
    {
        Console.WriteLine(vapros + " (да/не)");
        string otgovor = Console.ReadLine();
        if (otgovor == null) return false;
        otgovor = otgovor.Trim().ToLower();
        if (otgovor == "да") return true;
        if (otgovor == "не") return false;
        Console.WriteLine("Не разбрах „" + otgovor + "“ — отговорете с да или не.");
    }
}

Какво трябва да запомниш

  • Дърво → вложени if/else: всеки въпрос е условие, всяко листо — присвояване на съвет и обосновка.
  • Правила като данни: клас с масив от условия и извод; масив от правила; цикъл, който спира на първото сработило.
  • Обосновката съдържа само фактите, от които зависи изводът — при правилата като данни идва наготово.
  • По-конкретните правила стоят преди по-общите — цикълът спира на първото.
  • Гранични случаи: главни букви и интервали, непознат отговор, свършил вход, нито едно сработило правило.