SWI-Prolog医疗诊断系统故障:诊断后跳转至症状问题而非治疗环节
问题描述
我正在开发一个基于SWI-Prolog的医疗诊断界面,核心功能是:向用户展示症状列表并询问是否存在(输入1表示“是”,输入2表示“否”),将症状关联到对应疾病后,提供包含治疗方法、成本及时长的诊疗方案。但在SWI-Prolog中运行代码时出现异常:系统能成功诊断出疾病(比如示例中的gripe),但不会进入治疗环节,反而跳回第15个症状问题(perg15)。我之前调整过代码,但又出现了新问题,作为Prolog新手,求具体的代码修改方案。
规则定义代码
:- dynamic (fact/1). :- dynamic (demo/1). %A simple backward chaining rule interpreter :- op( 800, fx, if). :- op( 700, xfx, then). :- op( 300, xfy, or). :- op( 200, xfy, and). demo( Q) :- fact( Q). demo( Q) :- if Condition then Q, %A relevant rule demo( Condition). %whose condition is true demo( Q1 and Q2) :- demo( Q1), demo( Q2). demo( Q1 or Q2) :- demo( Q1) ; demo( Q2). %Simple forward chaining in Prolog demo:- new_derived_fact( P), %A new fact !, write( 'Derived: '), write( P), nl, assert( fact( P)), demo. demo:- write( 'No more facts'). %All facts derived new_derived_fact( Concl) :- if Cond then Concl, %A rule \+ fact( Concl), %Rule's conclusion not yet a fact composed_fact( Cond). %Condition true? composed_fact( Cond) :- fact( Cond). %Simple fact composed_fact( Cond1 and Cond2) :- composed_fact( Cond1), composed_fact( Cond2). %Both conjuncts true composed_fact( Cond1 or Cond2) :- composed_fact( Cond1) ; composed_fact( Cond2). %Associating symptoms with diseases if febre and tosse and dor_de_cabeca and cansaco then gripe. if cansaco and palidez then anemia. if dificuldade_respiratoria and tosse then asma. if dor_abdominal and nausea and diarreia and vomito then gastroenterite. if dor_de_cabeca and nausea and sensibilidade_a_luz then migranea. if febre and dor_muscular and dor_de_cabeca and erupcoes_cutaneas then dengue. if dor_abdominal and febre and nausea and perda_de_apetite then apendicite. if febre and tosse and dificuldade_respiratoria and dor_no_peito then pneumonia. if febre and dor_de_cabeca and cansaco and dor_de_garganta and inchaco_nos_ganglios then mono. if cansaco and aumento_de_peso and intolerancia_ao_frio then hipotireoidismo. if perda_de_peso and taquicardia and nervosismo and intolerancia_ao_calor then hipertireoidismo. if sede_excessiva and fome_frequente and urinacao_frequente and perda_de_peso then diabetes. if dor_lombar and rigidez_muscular then lombalgia. if erupcoes_cutaneas and coceira and vermelhidao_na_pele then urticaria. if dor_abdominal and diarreia and perda_de_peso and presenca_de_sangue_nas_fezes then colite_ulcerativa. if tosse and tosse_prolongada and febre and suores_noturnos and perda_de_peso then tuberculose. if dor_de_cabeca and tontura and falta_de_ar then hipertensao. if dificuldade_respiratoria and cansaco and inchaco_nas_pernas and tosse and tosse_seca then insuficiencia_cardiaca. %Treatments tratamento(gripe, [repouso, hidratacao, paracetamol], 7, 7). tratamento(anemia, [dieta, suplemento_ferro], 15, 15). tratamento(asma, [inalador], 40, 10). tratamento(gastroenterite, [hidratacao, dieta], 0, 30). tratamento(migranea, [aspirina, repouso], 10, 10). tratamento(dengue, [hidratacao, paracetamol], 3, 15). tratamento(apendicite, [cirurgia], 1500, 1). tratamento(pneumonia, [antibiotico], 10, 15). tratamento(mono, [hidratacao, repouso], 0, 10). tratamento(hipotireoidismo, [terapia_hormonal], 90, 3). tratamento(hipertireoidismo, [antitireoidiano], 35, 5). tratamento(diabetes, [dieta, exercicio, insulina], 15, 5). tratamento(lombalgia, [fisioterapia, brufen], 10, 15). tratamento(urticaria, [antihistaminico], 20, 5). tratamento(colite_ulcerativa, [antiinflamatorio], 15, 7). tratamento(tuberculose, [antibiotico], 30, 30). tratamento(hipertensao, [dieta, antihipertensivo], 5, 15). tratamento(insuficiencia_cardiaca, [dieta, vasodilatador, diuretico], 5, 15).
界面运行代码
:- dynamic (fact/1). :- dynamic (demo/1). %Main Function to execute consultation consulta :- iniciar_consulta, doenca, tratamento. %Iniciate consultation iniciar_consulta :- retractall(fact(_)), writeln('Bem-vindo ao sistema de diagnostico medico.'), writeln('Por favor, responda às seguintes perguntas com 1 (sim) ou 2 (nao):'), perg1. perg1 :- writeln('Tem febre?'), read(S), (S == 1, assert(fact(febre)), perg2); (S \== 1, perg2). perg2 :- writeln('Tem tosse?'), read(S), (S == 1, assert(fact(tosse)), perg3); (S \== 1, perg3). perg3 :- writeln('Tem tosse seca?'), read(S), (S == 1, assert(fact(tosse_seca)), perg4); (S \== 1, perg4). perg4 :- writeln('Tem tosse prolongada?'), read(S), (S == 1, assert(fact(tosse_prolongada)), perg5); (S \== 1, perg5). perg5 :- writeln('Tem cansaço?'), read(S), (S == 1, assert(fact(cansaco)), perg6); (S \== 1, perg6). perg6 :- writeln('Tem nervosismo?'), read(S), (S == 1, assert(fact(nervosismo)), perg7); (S \== 1, perg7). perg7 :- writeln('Tem naúseas?'), read(S), (S == 1, assert(fact(nausea)), perg8); (S \== 1, perg8). perg8 :- writeln('Tem diarreia?'), read(S), (S == 1, assert(fact(diarreia)), perg9); (S \== 1, perg9). perg9 :- writeln('Tem vómitos?'), read(S), (S == 1, assert(fact(vomito)), perg10); (S \== 1, perg10). perg10 :- writeln('Tem coceira?'), read(S), (S == 1, assert(fact(coceira)), perg11); (S \== 1, perg11). perg11 :- writeln('Tem taquicardia?'), read(S), (S == 1, assert(fact(taquicardia)), perg12); (S \== 1, perg12). perg12 :- writeln('Tem tonturas?'), read(S), (S == 1, assert(fact(tontura)), perg13); (S \== 1, perg13). perg13 :- writeln('Tem dor abdominal?'), read(S), (S == 1, assert(fact(dor_abdominal)), perg14); (S \== 1, perg14). perg14 :- writeln('Tem dor de cabeca?'), read(S), (S == 1, assert(fact(dor_de_cabeca)), perg15); (S \== 1, perg15). perg15 :- writeln('Tem dor muscular?'), read(S), (S == 1, assert(fact(dor_muscular)), perg16); (S \== 1, perg16). perg16 :- writeln('Tem dor no peito?'), read(S), (S == 1, assert(fact(dor_no_peito)), perg17); (S \== 1, perg17). perg17 :- writeln('Tem dor de garganta?'), read(S), (S == 1, assert(fact(dor_de_garganta)), perg18); (S \== 1, perg18). perg18 :- writeln('Tem dificuldade respiratória?'), read(S), (S == 1, assert(fact(dificuldade_respiratoria)), perg19); (S \== 1, perg19). perg19 :- writeln('Tem rigidez muscular?'), read(S), (S == 1, assert(fact(rigidez_muscular)), perg20); (S \== 1, perg20). perg20 :- writeln('Tem perda de apetite?'), read(S), (S == 1, assert(fact(perda_de_apetite)), perg21); (S \== 1, perg21). perg21 :- writeln('Tem inchaço nos gânglios?'), read(S), (S == 1, assert(fact(inchaco_nos_ganglios)), perg22); (S \== 1, perg22). perg22 :- writeln('Tem inchaço nas pernas?'), read(S), (S == 1, assert(fact(inchaco_nas_pernas)), perg23); (S \== 1, perg23). perg23 :- writeln('Tem erupções cutâneas?'), read(S), (S == 1, assert(fact(erupcoes_cutaneas)), perg24); (S \== 1, perg24). perg24 :- writeln('Tem vermelhidão na pele?'), read(S), (S == 1, assert(fact(vermelhidao_na_pele)), perg25); (S \== 1, perg25). perg25 :- writeln('Tem sensibilidade à luz?'), read(S), (S == 1, assert(fact(sensibilidade_a_luz)), perg26); (S \== 1, perg26). perg26 :- writeln('Tem perdido peso?'), read(S), (S == 1, assert(fact(perda_de_peso)), perg27); (S \== 1, perg27). perg27 :- writeln('Tem ganho peso?'), read(S), (S == 1, assert(fact(aumento_de_peso)), perg28); (S \== 1, perg28). perg28 :- writeln('Tem intolerância ao frio?'), read(S), (S == 1, assert(fact(intolerancia_ao_frio)), perg29); (S \== 1, perg29). perg29 :- writeln('Tem intolerância_ao_calor?'), read(S), (S == 1, assert(fact(intolerancia_ao_calor)), perg30); (S \== 1, perg30). perg30 :- writeln('Tem urinado frequentemente?'), read(S), (S == 1, assert(fact(urinacao_frequente)), perg31); (S \== 1, perg31). perg31 :- writeln('Tem sede excessiva?'), read(S), (S == 1, assert(fact(sede_excessiva)), perg32); (S \== 1, perg32). perg32 :- writeln('Tem fome frequente?'), read(S), (S == 1, assert(fact(fome_frequente)), perg33); (S \== 1, perg33). perg33 :- writeln('Tem presenca de sangue nas fezes?'), read(S), (S == 1, assert(fact(presenca_de_sangue_nas_fezes)), perg34); (S \== 1, perg34). perg34 :- writeln('Tem suores noturnos?'), read(S), (S == 1, assert(fact(suores_noturnos)), perg35); (S \== 1, perg35). perg35 :- writeln('Tem falta de ar?'), read(S), ((S == 1, assert(fact(falta_de_ar)), doenca); (S \== 1, doenca)). %Diagnosting the disease doenca :- demo, writeln('Baseado nos sintomas fornecidos, pode ter:'), findall(X, fact(X), Doencas), last_element(Doencas, Doenca), writeln(Doenca), tratamento. last_element(List, Last) :- last(List, Last). %Providing treatment for the determined disease tratamento :- demo, %sistema de inferência forward, para inferir a doença. fact(Doenca), tratamento(Doenca, [Tratamentos], Custo, Duracao), writeln('Tratamento recomendado:'), write('Para tratar '), write(Doenca), write(' recomendamos o seguinte tratamento: '), writeln([Tratamentos]), writeln('Este tratamento terá um custo total de: '), writeln(Custo), writeln('E uma duração (em dias) de: '), writeln(Duracao).
问题根源与修改方案
核心问题
- 流程重复触发:
consulta/0中调用iniciar_consulta后又重复调用doenca和tratamento,但iniciar_consulta的perg35已经触发了doenca,加上Prolog的回溯机制,会回到之前的选择点(比如perg15)。 - 治疗谓词冲突:
tratamento/0与存储治疗信息的tratamento/4事实重名,导致匹配逻辑混乱。 - 疾病筛选逻辑错误:
doenca/0中用last_element取最后一个事实,可能取到的是症状而非推导的疾病;tratamento/0中fact(Doenca)会匹配所有事实(包括症状),而非仅疾病。 - 回溯点过多:症状提问中用
;分支,产生不必要的回溯点,导致流程跳转。
具体修改步骤
1. 简化主流程
移除consulta/0中的重复调用,让流程从iniciar_consulta自然进入诊断:
consulta :- iniciar_consulta.
2. 统一症状提问的结束逻辑
修改perg35,避免分支调用doenca,减少回溯点:
perg35 :- writeln('Tem falta de ar?'), read(S), (S == 1 -> assert(fact(falta_de_ar)) ; true), doenca.
同时将所有perg谓词的分支逻辑替换为->(比如perg1):
perg1 :- writeln('Tem febre?'), read(S), (S == 1 -> assert(fact(febre)) ; true), perg2.
3. 修复疾病诊断逻辑
筛选出真正的疾病事实,而非所有症状,并传递给治疗环节:
doenca :- demo, writeln('Baseado nos sintomas fornecidos, pode ter:'), % 只筛选通过规则推导的疾病 findall(Doenca, (if _ then Doenca, fact(Doenca)), Doencas), (Doencas = [] -> writeln('Nenhuma doença detectada
相关产品推荐
相关产品推荐

