Это будет работать, но позволяет вводить повторяющиеся симптомы.Я публикую его сейчас, чтобы вы могли увидеть первую часть преобразования и опубликую часть без дубликатов, когда я получу эту работу.
getSymptoms(Symptoms) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Symptoms = []
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms)
).
valid_symptom(Symptom,Symptoms) :-
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list on backtracking
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list on backtracking.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
).
Поскольку введенные значения являются строками, а симптомы - атомами в symptom/2
фактах, входные данные необходимо преобразовать в атом для сравнения.Это делается с помощью atom_string / 2
atom_string(Symptom,Response)
Чтобы дать пользователю обратную связь, если признак недействителен, используется format / 2 .Лучше использовать, чем write / 1 , поскольку это дает вам больший контроль над выводом.
format('Invalid symptom: `~w''~n',[Symptom])
Если для симптома введено недопустимое значение, его не следует добавлять ксписок.Это классический сценарий типа if / then, а в Prolog это делается с помощью -> / 2 .Вот стандартный шаблон
(
<conditional>
->
<true branch>
;
<false branch>
)
, условное выражение
symptom(_,Symptom)
Обратите внимание также на условное условие, что он считывает факты symptom/2
и игнорирует первую часть составной структуры,то есть _
, и сопоставляет входной симптом с симптомом в фактах.Это сравнение выполняется, но выполняется с помощью объединения, а не предиката сравнения, например == / 2 .
Истинная ветвь такая же, как и раньше
getSymptoms(Symptoms0),
Symptoms = [Symptom|Symptoms0]
однако ложная ветвь равна
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms(Symptoms0),
Symptoms = Symptoms0
Обратите внимание, что неверный Symptom
не добавлен в список с [Symptom|Symptoms0]
, а также что обе ветви (истина и ложь) должны обновитьте же переменные, Symptoms
, которые в ложной ветви выполняются с Symptoms = Symptoms0
, который не является присваиванием, но = / 2 (объединение).
Код для valid_symptom/2
может иметьбыло добавлено getSymptoms/1
, но я вытащил его, чтобы вы могли видеть, как это делается на случай, если вам понадобится сделать это в будущем.
Пример выполнения:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: wrong
Invalid symptom: `wrong'
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [headache, malaise, headache].
Вот следующий вариант, который удаляет дубликаты при построении списка.
getSymptoms(Result) :-
getSymptoms_helper([],Result).
getSymptoms_helper(Symptoms,Result) :-
write('Please enter symptoms now, enter "Done" when finished: ' ),
read_string(user, "\n", "\r", _, Response),
(
Response == "Done"
->
Result = Symptoms
;
atom_string(Symptom,Response),
valid_symptom(Symptom,Symptoms,Result)
).
valid_symptom(Symptom,Symptoms,Result) :-
(
memberchk(Symptom,Symptoms)
->
% Symptom was a duplicate
% Do not add duplicate Symptom to list.
getSymptoms_helper(Symptoms,Result)
;
(
symptom(_,Symptom)
->
% Symptom was valid
% so get next symptom and
% add to list.
getSymptoms_helper([Symptom|Symptoms],Result)
;
% Symptom was invalid
% so warn user of invalid symptom and what they input
% and get next symptom.
% Do not add invalid Symptom to list.
format('Invalid symptom: `~w''~n',[Symptom]),
getSymptoms_helper(Symptoms,Result)
)
).
Основное изменение здесь состоит в том, что аккумулятор Symptoms
пронизывает предикаты, чтобы можно было составить список допустимых симптомов ииспользуется для проверки следующего входного значения.Так как аккумулятор должен быть инициализирован при запуске, предыдущий предикат был переименован в getSymptoms_helper
, чтобы аккумулятор мог инициализироваться с помощью
getSymptoms_helper([],Result)
Уведомления []
, который передается в
getSymptoms_helper(Symptoms,Result)
, таким образом, устанавливая начальное значение Symptoms
в []
.
Когда введено Done
, список объединяется с Result
и передается обратно при обратной цепочке.Обычно переменные называются Symptoms0
и Symptoms
, но я сохранил их таким образом, чтобы им было легче следить.В противном случае могут быть переменные Symptom
, Symptoms
и Symptoms0
, но после того, как вы к ним привыкнете, вам будет легче следить.
Проверка на наличие дубликатов выполняется с помощью memberchk / 2 , что лучше, чем использовать member / 2 для проверки.Опять же, это добавляет еще одно условие к миксу.
Пример выполнения:
?- getSymptoms(Symptoms).
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: malaise
Please enter symptoms now, enter "Done" when finished: headache
Please enter symptoms now, enter "Done" when finished: Done
Symptoms = [malaise, headache].