------------------------------------------------------------------------ -- Пример визуальной экспертной системы. -- -- Программа написана на Акторном Прологе. -- -- (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). ] ------------------------------------------------------------------------ |