Prolog Recursion пропускает те же результаты - PullRequest
4 голосов
/ 29 ноября 2011

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

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]
    ).

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

Ответы [ 3 ]

7 голосов
/ 29 ноября 2011

Ваша программа имеет чистое ядро ​​- или придерживаться медицинских терминов - чистое сердце, но это переплетено с раковой тканью ввода / вывода! Таким образом, сделать это правильно очень сложно, если не невозможно. Например, в качестве незначительной ошибки ваша программа завершается с ошибкой 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, ..., _) вокруг своей цели. Вы можете использовать это всякий раз, когда ответы являются наземными. То есть в ответе не осталось переменной.

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

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

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

Что касается нечистой части, пожалуйста, посмотрите на подход @ Mog.

3 голосов
/ 29 ноября 2011

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

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).

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

2 голосов
/ 29 ноября 2011

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

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

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: без разреза вы можете получить более одного решения, каждое из которых представляет различный набор заболеваний, соответствующих набору симптомов.Если вы добавите сокращение, вы получите только первое решение (включая беременность).Второе решение содержит только грипп и ВИЧ.

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

...