------------------------------------------------------------------------
--                Пример визуальной экспертной системы.               --
--               Программа написана на Акторном Прологе.              --
--              (c) Алексей А. Морозов. 19 марта 1999 г.              -
------------------------------------------------------------------------
-- Это моя самая любимая программа. Она помогает варить варенье из    --
-- чёрной рябины, яблок и земляники. На самом деле, когда я писал эту --
-- программу, мне хотелось потренироваться в проведении интервью по   --
-- правилам SADT методологии. Для этого я не придумал ничего лучше,   --
-- как попросить мою собственную матушку рассказать мне, как надо     --
-- варить варенье. Поскольку я старался нарисовать модель как можно   --
-- более подробно, интервью затянулось на два дня. Результат оказался --
-- неожиданным для нас обоих. Матушка была очень удивлена, когда я    --
-- показал ей полный перечень посуды и инструментов, которые ей       --
-- приходится использовать во время приготовления обычного варенья.   --
-- Заодно я немного научился готовить. Матушка, спасибо за помощь!    --
------------------------------------------------------------------------
-- Логическое описание блоков SADT модели.                            --
------------------------------------------------------------------------
-- Этот класс просто заглушка, вызывающая диалоговое окно             --
-- выбора ягод и фруктов.                                             --
------------------------------------------------------------------------
class 'Выбрать_ягоды_и_фрукты' specializing 'DIALOG':
value_o1;
identifier= "ChooseFruits";
[
]
------------------------------------------------------------------------
-- Далее следуют классы, соответствующие блокам SADT модели.          --
-- Основное предназначение этих классов - хранить перечень посуды и   --
-- инструментов, соответствующих различным блокам SADT модели.        --
-- В дополнение к этому некоторые блоки:                              --
-- 1) Передают на выход значения (наименования выбранных ягод и       --
--    фруктов), полученные на входе.                                  --
-- 2) Проверяют, соответствуют ли выбранные ягоды и фрукты данному    --
--    блоку. Если не соответствуют, исполнение актора goal            --
--    заканчивается неудачей, процесс переходит в состояние           --
--    "неудачный", и соответствующий блок на экране краснеет.         --
-- 3) Вычисляют рекомендуемое время нагрева сахарной смеси для        --
--    выбранных ягод и фруктов.                                       --
-- Классы, соответствующие составным блокам, являются потомками       --
-- класса 'Compound_Block', назначение которого будет рассмотрено     --
-- ниже, когда мы будем обсуждать устройство блока "Анализ модели".   --
------------------------------------------------------------------------
class 'Вымыть_банки_и_крышки' specializing 'Simple_Block':
[
]
------------------------------------------------------------------------
class 'Вскипятить_воду_в_чайнике' specializing 'Simple_Block':
[
instrument()= "Чайник".
]
------------------------------------------------------------------------
class 'Обработать_крышки_кипятком' specializing 'Simple_Block':
[
instrument()= "Кастрюля для кипячения воды".
]
------------------------------------------------------------------------
class 'Обработать_банки_кипятком' specializing 'Simple_Block':
[
instrument()= "Чайник".
]
------------------------------------------------------------------------
class 'Просушить_банки_и_крышки' specializing 'Simple_Block':
[
]
------------------------------------------------------------------------
class 'Вымыть_ягоды_и_фрукты' specializing 'Simple_Block':
value_i1;
protecting: value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Дуршлаг".
]
------------------------------------------------------------------------
class 'Подготовить_смесь_из_твёрдых_ягод_и_сахара'
specializing 'Compound_Block':
value_i1;
[
goal:-!,
        check_fruits(value_i1).
--
check_fruits("Чёрная рябина").
]
------------------------------------------------------------------------
class 'Подготовить_смесь_из_крупных_фруктов_и_сахара'
specializing 'Compound_Block':
value_i1;
[
goal:-!,
        check_fruits(value_i1).
--
check_fruits("Яблоки \"Антоновка\"").
]
------------------------------------------------------------------------
class 'Подготовить_смесь_из_мягких_ягод_и_сахара'
specializing 'Compound_Block':
value_i1;
[
goal:-!,
        check_fruits(value_i1).
--
check_fruits("Земляника").
]
------------------------------------------------------------------------
class 'Вскипятить_воду_в_кастрюле' specializing 'Simple_Block':
[
instrument()= "Кастрюля для кипячения воды".
]
------------------------------------------------------------------------
class 'Приготовить_сироп' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Кастрюля или медная миска".
instrument()= "Деревянная ложка".
]
------------------------------------------------------------------------
class 'Бланшировать_ягоды' specializing 'Simple_Block':
value_i2;
value_o1;
[
goal:-!,
        value_o1 == value_i2.
--
instrument()= "Дуршлаг".
instrument()= "Кастрюля для кипячения воды".
]
------------------------------------------------------------------------
class 'Положить_ягоды_в_сироп' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Порезать_на_дольки' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Нож из нержавеющей стали".
]
------------------------------------------------------------------------
class 'Пересыпать_сахаром' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Выстаивать_6-8_часов' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Добавить_в_смесь_воду' specializing 'Simple_Block':
value_i1;
value_o1;
[
goal:-!,
        value_o1 == value_i1.
--
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Цикл_варки_твёрдых_ягод_и_фруктов'
specializing 'Compound_Block':
value_i1;
[
goal:-!,
        check_fruits(value_i1).
--
check_fruits("Чёрная рябина").
check_fruits("Яблоки \"Антоновка\"").
]
------------------------------------------------------------------------
class 'Цикл_варки_мягких_ягод_и_фруктов'
specializing 'Compound_Block':
value_i1;
[
goal:-!,
        check_fruits(value_i1).
--
check_fruits("Земляника").
]
------------------------------------------------------------------------
class 'Нагреть_смесь' specializing 'DIALOG':
value_i2;
time;
identifier      = "HeatUpMixture";
fruits          = value_i2;
[
goal:-!,
        set_time(value_i2,time).
--
set_time(#,"3-5"):-!.
set_time("Чёрная рябина","3"):-!.
set_time("Яблоки \"Антоновка\"",5):-!.
set_time("Земляника","---"):-!.
set_time(_,"3-5"):-!.
--
instrument()= "Кастрюля или медная миска".
instrument()= "Деревянная ложка".
]
------------------------------------------------------------------------
class 'Выстаивание' specializing 'Simple_Block':
[
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Проверить_готовность_варенья' specializing 'Simple_Block':
[
instrument()= "Деревянная ложка".
instrument()= "Блюдце".
]
------------------------------------------------------------------------
class 'Основной_этап_варки' specializing 'DIALOG':
value_i1;
time;
identifier      = "HeatUpMixture";
fruits          = value_i1;
[
goal:-!,
        set_time(value_i1,time).
--
set_time(#,"---"):-!.
set_time("Земляника","10"):-!.
set_time(_,"---"):-!.
--
instrument()= "Кастрюля или медная миска".
instrument()= "Деревянная ложка".
]
------------------------------------------------------------------------
class 'Дополнительный_этап_варки' specializing 'Simple_Block':
[
instrument()= "Кастрюля или медная миска".
instrument()= "Деревянная ложка".
]
------------------------------------------------------------------------
class 'Охладить_варенье' specializing 'Simple_Block':
[
instrument()= "Кастрюля или медная миска".
]
------------------------------------------------------------------------
class 'Разлить_варенье_в_банки' specializing 'Simple_Block':
[
instrument()= "Деревянная ложка".
instrument()= "Машинка для закатки".
]
------------------------------------------------------------------------
class 'Проверить_готовность_варенья_(твёрдые_ягоды_и_фрукты)'
specializing 'Simple_Block':
[
instrument()= "Блюдце".
]
------------------------------------------------------------------------
class 'Проверить_готовность_варенья_(мягкие_ягоды)'
specializing 'Simple_Block':
[
instrument()= "Блюдце".
]
------------------------------------------------------------------------
class 'Simple_Block':
[
goal.
--
show(_).
]
------------------------------------------------------------------------
-- Этот блок строит и выводит на экран полный перечень посуды и       --
-- инструментов, необходимых для приготовления варенья из выбранных   --
-- ягод и фруктов. Принцип работы блока состоит в следующем:          --
-- 1) Слот container, автоматически создаваемый во время трансляции   --
--    SADT диаграмм, содержит процесс, соответствующий блоку,         --
--    объемлющему данный, то есть блоку "Приготовление варенья".      --
--    С помощью специального резидента в этом блоке доказывается      --
--    предикат instrument.                                            --
-- 2) Блок "Приготовление варенья", а также все остальные составные   --
--    блоки в ходе трансляции модели реализуются с помощью классов.   --
--    Все эти классы являются потомками класса 'Compound_Block'.      --
-- 3) Класс 'Compound_Block' определён в тексте программы. При этом   --
--    в классе 'Compound_Block' определена функция instrument, таким  --
--    образом что её значением является значение функции component.   --
-- 4) Функция component является недетерминированной и определяется   --
--    автоматически в ходе трансляции всех составных блоков.          --
--    В качестве значения эта функция возвращает процессы,            --
--    соответствующие блокам, входящим в состав рассматриваемого      --
--    составного блока.                                               --
-- 5) Таким образом, с помощью резидента блок "Анализ модели"         --
--    получает список процессов, соответствующих соседним блокам.     --
-- 6) Полученный список передаётся другому резиденту в рекурсивном    --
--    определении процесса 'Instruments', который доказывает в этих   --
--    мирах тот же самый предикат instrument.                         --
-- 7) Если предикат instrument доказывается в простом блоке,          --
--    соответствующий процесс возвращает наименования посуды и        --
--    инструментов, относящихся к данному блоку. Если предикат        --
--    доказывается в составном блоке, исполнение предиката происходит --
--    так же как в блоке "Приготовление варенья", рассмотренном       --
--    выше. Таким образом, резидент вычисляет список, в состав        --
--    которого могут входить как списки посуды и инструментов, так и  --
--    процессы, соответствующие некоторым блокам.                     --
-- 8) Полученный список передаётся на следующий виток рекурсии и      --
--    обрабатывается следующим резидентом. Такая рекурсивная          --
--    обработка списка продолжается до тех пор, пока на очередном     --
--    витке список не окажется равным списку, поступившему на         --
--    предыдущий уровень рекурсии. Если список окажется равным, это   --
--    означает, что просмотр дерева блоков достиг всех его вершин, и  --
--    в составе списка больше нет процессов.                          --
-- 9) Вычисленный таким образом список посуды и инструментов          --
--    сортируется с удалением повторов и выводится на экран с помощью --
--    диалогового окна.                                               --
------------------------------------------------------------------------
class 'Анализ_модели:_Какие_необходимы_посуда_и_инструменты?'
specializing 'DIALOG':
--
identifier= "ListOfInstruments";
--
title;
name;
number;
model;
x;
y;
text_color;
container;
con;
--
result;
instruments;
--
agent   = (('FindInstruments',
                        container,
                        protecting: result));
[
goal:-
        refine_list(result,[],instruments),!.
goal:-!,
        [result].
--
refine_list([],List,List):-!.
refine_list([#|Rest],List1,List2):-!,
        refine_list(Rest,List1,List2).
refine_list([Item|Rest],List1,List3):-
        is_list(Item),!,
        refine_list(Item,List1,List2),
        refine_list(Rest,List2,List3).
refine_list([Item|Rest],List1,List2):-
        is_not_element(Item,List1),!,
        refine_list(Rest,[Item|List1],List2).
refine_list([_|Rest],List1,List2):-!,
        refine_list(Rest,List1,List2).
refine_list(_,List,List).
--
is_list([]):-!.
is_list([_|_]).
--
is_not_element(_,[]):-!.
is_not_element(Item,[Item|_]):-!,
        fail.
is_not_element(Item,[_|List]):-
        is_not_element(Item,List).
--
show(_):-
        show.
]
------------------------------------------------------------------------
class 'FindInstruments' specializing 'ALPHA':
container;
result;
agent   = (('Instruments',
                suspending: source_list= [],
                target_list= container,
                protecting: result
                ));
[
goal:-!.
]
------------------------------------------------------------------------
class 'Instruments' specializing 'Alpha':
source_list;
target_list;
result;
--
tail_list;
tail_result;
tail    = (('Instruments',
                suspending: source_list=tail_list,
                target_list=target_list ?? instrument(),
                protecting: result=tail_result
                ));
con     = ('Console');
[
goal:-
        source_list == target_list,!,
        result == target_list.
goal:-
        tail_list== target_list,
        result== tail_result.
]
------------------------------------------------------------------------
class 'Compound_Block' specializing 'Alpha':
c = ('Console');
[
instrument()= Block
        :-!,
        component(Block).
]
------------------------------------------------------------------------