2011-11-29 2 views
4

Мой код работает, но проблема в том, что он показывает одни и те же результаты более одного раза. Вот мой код:Пролог Рекурсия пропускает те же результаты

disease(hiv,[sore_throat,headache,fever,rash]). 
disease(pregnancy,[fatigue,vomiting,light_headedness,increased_waistline]). 
disease(flu,[fatigue,fever,tiredness,nasal_discharge]). 

diagnose([], []). 
diagnose(Name, [H|T]) :- 
    disease(The_Disease, Symptoms), 
    member(H, Symptoms), 
    write(Name), write(' has/is '), writeln(The_Disease), 
    diagnose(Name, T). 

member(X,[X|_]). 
member(X,[_|T]):- 
    member(X,T). 

Результат при выполнении в прологе:

?- diagnose(kevin,[sore_throat,fatigue,tiredness,rash]). 
kevin has/is hiv 
kevin has/is pregnancy 
kevin has/is flu 
kevin has/is hiv 
kevin has/is flu 
kevin has/is flu 
kevin has/is hiv 
false. 

Как избежать этого же результаты? Я пробовал использовать другой метод, который я нашел здесь:

filter_doubles([], []). 
filter_doubles([X|L], Result) :- 
    (memberchk(X,L) -> 
     filter_doubles(L, Result) 
    ; 
     filter_doubles(L, Result0), 
     Result = [X|Result0] 
    ). 

Но я не смог применить его к своему коду. Помоги пожалуйста.

+0

Возможно ли это, что вы проверяете * все * симптомы болезни присутствуют, прежде чем возвращать этот диагноз? – hardmath

+0

@ hardmath да, это план. – Mezzan

ответ

6

Ваша программа имеет чистое сердцевину - или придерживаться медицинских терминов - чистое сердце, но это переплетено с раковой ткацкой сетью! Таким образом, делать это правильно очень сложно, если не невозможно. Например, в качестве незначительной ошибки ваша программа не работает для kevin. Но вы, вероятно, намеревались добиться успеха. С другой стороны, вы добьетесь успеха таинственного господина []! Кто это?

Так что отделяйте чисто от нечистого!

Чистая часть вашей программы связана с тем, чтобы связать список симптомов с возможными диагнозами. Ваше рабочее предположение заключается в том, что если есть один признак, который является частью показаний к болезни, мы будем диагностировать эту болезнь - просто чтобы быть уверенным. Так почему бы не назвать это symptoms_diagnosis/2?

symptoms_diagnosis(Symptoms, Diagnosis) :- 
    member(Symptom, Symptoms), 
    disease(Diagnosis, Indications), 
    member(Symptom, Indications). 

?- symptoms_diagnosis([sore_throat,fatigue,tiredness,rash], Diagnosis). 
Diagnosis = hiv ; 
Diagnosis = pregnancy ; 
Diagnosis = flu ; 
Diagnosis = flu ; 
Diagnosis = hiv ; 
false. 

Обратите внимание, что даже без каких-либо дальнейших проволочек, мы имеем менее избыточных решений, чем в исходной программе. Итак, как избавиться от оставшихся избыточных решений? Это делает трюк:

?- setof(t,symptoms_diagnosis([sore_throat,fatigue,tiredness,rash], Diagnosis),_). 
Diagnosis = flu ; 
Diagnosis = hiv ; 
Diagnosis = pregnancy. 

Таким образом, когда вы получаете лишние решения, просто обернуть setof(t, ..., _) вокруг своей цели. Вы можете использовать это, когда ответы на них основаны на ответах. То есть в ответе нет переменной.

Возможно, вы предпочитаете, чтобы диагноз был указан в списке?

Итак, теперь мы готовы к госпиталю Принстон-Плейнсборо. Это просто суеверие, если доктор Хаус не примет диагноз Пролога!

Для нечистой части, пожалуйста, взгляните на подход @ Мога.

+1

Спасибо @false! С вашим объяснением я начинаю понимать пролог лучше. Извините за тайну [], где это должно быть Имя вместо. Да, я предпочитаю поставить диагноз в свой собственный список. – Mezzan

+1

Добро пожаловать! Изучая Prolog, старайтесь держаться подальше от побочных эффектных встроенных модулей как можно дольше. Это чистая часть Пролога, которая делает его настолько интересным. – false

2

Вы должны помнить, какие заболевания вы уже собрали при проверке симптомов. Вам необходимо собрать (заполнить) болезни в списке и проверить, присутствует ли заболевание в списке, прежде чем добавлять его. Затем вы можете распечатать список в конце или распечатать каждое новое заболевание, поскольку оно добавлено в список.

Я бы реализовать это следующим образом:

diagnose(Name, Symptoms) :- diagnose0(Name, Symptoms, []). 

diagnose0(Name, [], Diseases) :- 
    print_diseases(Name, Diseases). 
diagnose0(Name, [H|T], DIn) :- 
    disease(Disease, Symptoms), 
    member(H, Symptoms), 
    % you may want to add a cut here to avoid choicepoints 
    (
     member(Disease, DIn) 
    -> 
     diagnose0(Name, T, DIn) 
    ; 
     DOut = [Disease|DIn], 
     diagnose0(Name, T, DOut) 
    ). 

print_diseases(_Name, []). 
print_diseases(Name, [H|T]) :- 
    write(Name), write(' has/is '), writeln(H), 
    print_diseases(Name, T). 

В disease/2 факты, как в вашем коде.

Это дает:

?- diagnose(kevin, [sore_throat, fatigue, tiredness, rash]). 
kevin has/is flu 
kevin has/is pregnancy 
kevin has/is hiv 
Yes (0.00s cpu, solution 1, maybe more) 

Следующий шаг, то, очевидно, будет найти способ, чтобы выразить, что некоторые диагнозы представляют альтернативы для данного симптома, и выбирать между этими различными альтернативами. С симптомами, перечисленными в запросе, у Кевина должны быть грипп и ВИЧ, но я сомневаюсь, что беременность является правильным диагнозом для Кевина. Это связано с комментарием о разрезе, которое я вставлял во второе предложение diagnose/3: без разреза вы можете получить более одного решения, каждый из которых представляет собой различный набор заболеваний, которые соответствуют набору симптомов. Если вы добавите разрез, вы получите только первое решение (включая беременность). Второе решение содержит только грипп и ВИЧ.

BTW, member/2 - это встроенный предикат, поэтому вам не нужно определять свои собственные.

3

В качестве альтернативы, вы можете написать:

disease(hiv,[sore_throat,headache,fever,rash]). 
disease(pregnancy,[fatigue,vomiting,light_headedness,increased_waistline]). 
disease(flu,[fatigue,fever,tiredness,nasal_discharge]). 

diagnose(Name, Symptoms) :- 
    findall(D, (disease(D, S), intersection(S, Symptoms, I), I \== []), MayGot), 
    atomic_concat(Name, ' has/is ', Start), 
    maplist(atomic_concat(Start), MayGot, Temp), 
    maplist(writeln, Temp). 

Но если вы пытаетесь узнать Пролог это не очень хорошая идея, так как это более Функциональное и менее Пролог-иш, думал, что я упомяну эту возможность в любом случае!

+3

Очень приятно! Я нахожу maplist/2 и maplist/3 очень Prolog-ish, чтобы сделать код более Prolog-ish, незначительное предложение: вместо болезни/2 мы могли бы назвать предикат, например, disease_symptoms/2 более описательным. – mat

+2

@Mog ** ** ** лучше выполнять операции ввода-вывода, как вы, а не «традиционный» путь в контурах, управляемых ошибками! Таким образом, ошибки в части ввода-вывода окажутся непреднамеренными. – false

+0

К ковку: Я пытался не переименовывать факты OP, но я согласен с тем, что болезни_симптомы будут более читабельными! К ложному: спасибо за то, что вышло! – m09

Смежные вопросы