Я собираюсь показать вам принципиально иной подход к этой программе, который использует Пролог немного лучше.Во-первых, давайте составим таблицу штрафов.Создание таблиц для конфигурации вашей программы часто полезно:
risk_penalty(overweight, 2).
risk_penalty(smoker, 1).
risk_penalty(diabetes, 3).
Теперь, когда у нас есть единое представление о проблеме, давайте посмотрим, сможем ли мы сделать единый способ получения информации изпользователь.Давайте используем динамическое хранилище для отслеживания того, что сказал нам пользователь, потому что это упростит запросы позже:
:- dynamic risk/2.
ask(Prompt, Fact) :-
format('~a [yes/no]> ', [Prompt]),
read(Response),
assertz(risk(Fact, Response)).
Теперь у нас есть небольшой предикат, который мы можем использовать для опроса пользователя.Такая функция print-read-assert довольно распространена в небольших экспертных системах, подобных вашей, потому что она помогает вам отделить логику системы от ее внешнего интерфейса.Когда вы делаете ask('Do you have X?', has_x)
, динамическое хранилище получает либо risk(has_x, yes)
, либо risk(has_x, no)
в зависимости от того, какой пользователь вошел.Это также дает вам естественное место, чтобы сделать пользовательский ввод более надежным, проверив его и повторно спросив, получаете ли вы что-то странное.
Теперь мы можем сделать ваш начальный цикл немного более чётким:
inicio :-
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes).
Это только часть интервью.Теперь, если вы пройдете через него один раз, ответив «да», «нет», «да», тогда база данных будет содержать следующие факты:
?- risk(Factor, Response).
Factor = overweight,
Response = yes ;
Factor = smoker,
Response = no ;
Factor = diabetes,
Response = yes.
Что нам нужно сделать сейчас, это выбрать «да»факторы, а затем посмотреть их штрафы и сложить их.Для этого мы можем использовать findall/3
, который берет шаблон, цель и возвращает список результатов:
?- findall(risk(Factor, Response), risk(Factor, Response), Responses).
Responses = [risk(overweight, yes), risk(smoker, no), risk(diabetes, yes)].
Как вы видите, я использовал здесь тот же шаблон и цель, просточтобы увидеть все результаты, но мы можем поставить «да», чтобы отфильтровать его до факторов риска, о которых мы заботимся:
?- findall(risk(Factor), risk(Factor, yes), Responses).
Responses = [risk(overweight), risk(diabetes)].
Теперь вы можете видеть, что Шаблон (первый аргумент) - это всего лишьпроизвольная структура, заполненная переменными, которые findall/3
нашли, запустив Goal
, второй аргумент.Таким образом, мы могли бы также просто получить список штрафных значений, если мы посмотрим их в запросе цели.Например:
?- findall(Penalty, %% <- template
(risk(Factor, yes), risk_penalty(Factor, Penalty)), %% <- goal
Penalties). %% <- result
Penalties = [2, 3].
Затем мы можем добавить всего 1030 *, чтобы сложить все:
?- findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)),
Penalties),
sumlist(Responsa, Score).
Responsa = [2, 3],
Score = 5.
Теперь мы можем завершить предикат inicio/0
:
inicio :-
retractall(risk(_, _)),
ask('Are you overweight?', overweight),
ask('Are you a smoker?', smoker),
ask('Do you have some direct relative with diabetes?', diabetes)
findall(Penalty,
(risk(Factor, yes), risk_penalty(Factor, Penalty)), Penalties),
sumlist(Penalties, Score),
format('The result is ~a~n', [Score]).
Теперь это выглядит так при запуске:
?- inicio.
Are you overweight? [yes/no]> yes.
Are you a smoker? [yes/no]> |: no.
Do you have some direct relative with diabetes? [yes/no]> |: yes.
The result is 5
true.
Надеюсь, вы найдете результат, на который приятно смотреть, гораздо менее процедурный, и его легче модифицировать и поддерживать.