/***************** Ein kleines System zur Datenbankabfrage mit natürlichspracher Ein- und Ausgabe für unser Sonnensystem Hier einige Beispielabfragen: A) Mit Zeichenkette als Frage (Frage in Anführungszeichen) (Zeichenkette wird intern in Liste umgewandelt, dann wie B)) ?- antworte("welcher astronom entdeckte uranus",Antwort). Antwort = "herschel entdeckt uranus" B) Mit Liste als Frage (Satz wird geparst, Semantik erzeugt, und Antwortsemantik ermittelt dann werden mögliche Antwortsätze generiert) ?- aw([welcher,astronom,entdeckte,uranus],Antwortliste). Antwortliste = [[herschel, entdeckt, uranus]] C) Mit Liste und der Ausgabe der berechneten Semantik ?- aw([welcher,astronom,entdeckte,uranus],SemantikFrage,Antwort,SemantikAntwort). SemantikFrage = frage(_G540, und(astronom(_G540), entdecken(_G540, uranus))) Antwort = [[herschel, entdeckt, uranus]] SemantikAntwort = [entdecken(herschel, uranus)] *****************/ /*Startprädikat, Variante 1 Frage ist eine Zeichenkette, etwa "welcher astronom entdeckte uranus" Die Anwort wird ebenfalls wieder in einen String umgewandelt VORSICHT: Es kann sein, dass die Prädikate satz_zu_atomliste/2 und antwortliste_zu_string/2 nicht in allen Prolog-Implementationen funktionieren Dann kann aber immer noch das Prädikat aw/2 statt diesem hier benutzt werden*/ antworte(Frage,AntwortString) :- satz_zu_atomliste(Frage,Frage1), aw(Frage1,Antworten), antwortliste_zu_string(Antworten,AntwortString). /*Startprädikate, Variante2 Die Frage muss hier bereits eine Liste sein*/ aw(Frage,Antworten) :- aw(Frage,_,Antworten,_). /*Startprädikat, Variante 3: mit Ausgabe der Semantik der Frage und der Semantiken der Antworten findall/3 ist ein vordefiniertes Prädikat, das alle möglichen Belegungen für eine Variable als Liste liefert (ähnlich bagof/3) uniq sorgt dafür, dass jede Antwortsemantik nur einmal verwendet wird, falls mehrmals dasselbe Ergebnis auftaucht*/ aw(Frage,Semantik,Antworten,AntwortSemantiken) :- satz(Semantik,Frage,[]), %syntaktische Analyse und Aufbau einer Semantik findall(AntwortSem,auswertung(Semantik,AntwortSem),AntwortSemantiken1), uniq(AntwortSemantiken1,AntwortSemantiken), formuliere_antworten(AntwortSemantiken,Antworten). /*Formuliere für jede Antwortsemantik eine Anwort */ formuliere_antworten([],[]). formuliere_antworten([Antwort_Semantik1|Rs],[Antwort1|Ra]) :- satz(Antwort_Semantik1,Antwort1,[]), !, %nur eine Antwort pro Semantik formuliere_antworten(Rs,Ra). /*Die Generierung des Satzes kann fehlschlagen (z.B. wegen fehlendem Lexikoneintrag), dann: Lasse diese Antwort aus*/ formuliere_antworten([_|Rs],Ra) :- %Antwort nicht formulierbar formuliere_antworten(Rs,Ra). /* Auswertung der Semantik vor der Datenbasis und Aufbau der Antwortsemantik Es wird das Prädikat auswertung/3 aufgerufen, das zusätzlich eine Liste der beim Antworten besetzten erfragten Variablen ausgibt; ist diese Liste leer, muss nur mit ja oder nein geantwortet werden.*/ auswertung(Semantik,Antwort_Semantik) :- auswertung(L_vars,Semantik,Antwort_Semantik_vorlaeufig), ist_antwort(L_vars,Antwort_Semantik_vorlaeufig,Antwort_Semantik). %Keine Variablen wurden beim Anwort-Prozess besetzt: %Gib nur "ja" aus ist_antwort([],_,wahr). %Variablen wurden besetzt: Gib die Semantik mit Belegungen als Antwortsemantik aus ist_antwort([_|_],Sem,Sem). %Eine W-Frage: gib aus, wonach gefragt wurde (die Auswertung von B) auswertung([X|Frage_Vars],frage(X,Y),Y1) :- !, auswertung(Frage_Vars,Y,Y1). %Auswertung eines Aussagesatzes (als ja/nein Frage interpretiert) auswertung(Frage_Vars,ex(_,Y),Antwort_Semantik) :- !, auswertung(Frage_Vars,Y,Antwort_Semantik). /*z.B. und(astronom(X),entdecken(X,uranus)): für die Antwort ist nur entdecken(X,uranus) relevant, der erste Teil (astronom(X)) wird nur abgeprüft.*/ auswertung(L,und(A,B),B1) :- !, auswertung(_,A,_), auswertung(L,B,B1). /*Letzte Möglichkeit: ein Prädikat wird ausgewertet: kopiere das Prädikat in die Antwort, nachdem es in der datenbasis instantiiert wurde*/ auswertung([],Praedikat,Praedikat) :- befrage_datenbasis(Praedikat). %Abbildung von Prädikaten auf die Datenbasis befrage_datenbasis(astronom(X)) :- db(_,_,_,X,_). befrage_datenbasis(gestirn(X)) :- db(X,_,_,_,_). befrage_datenbasis(umkreisen(X,Y)) :- db(X,_,_,_,Y). befrage_datenbasis(entdecken(X,Y)) :- db(Y,_,_,X,_). befrage_datenbasis(durchmesser(X,km(Y))) :- db(X,_,Y,_,_). befrage_datenbasis(planet(X)) :- db(X,planet,_,_,_). befrage_datenbasis(mond(X)) :- db(X,mond,_,_,_). /********************** Die Syntax mit Semantik ***********************/ %zur Einfacheren Abfrage satz(Sem,Satz) :- satz(Sem,Satz,[]). satz(Sem) --> antwortpartikel(Sem). satz(Sem) --> np(nom,lambda(Sem_vp,Sem)), vp(Sem_vp). %Sätze wie "Ist Mars ein Planet?" (Übungsaufgabe 9) satz(Sem) --> [ist], np(nom,lambda(Sem_N,Sem)), det([nom,G],indef,_), n([nom,G],Sem_N). %Verbalphrasen vp(Sem) --> intransitives_verb(Sem). vp(lambda(X,Sem_vp)) --> transitives_verb(lambda(X,Sem_v)), np(akk,lambda(Sem_v,Sem_vp)). vp(Sem_stvk) --> stuetzverb, np_pr(akk,Sem_stvk). %Nominalphrasen %herschel, uranus ... np(_,lambda(lambda(Sem,X),X)) --> eigenname(Sem). %wer, wen ... np(Kasus,lambda(lambda(Z,X),frage(Z,X))) --> fragepronomen(Kasus). %Voll-NP: ein gestirn, ein astronom etc. np(Kasus,Sem_np) --> det([Kasus,Genus],_,lambda(Sem_n,Sem_np)), n([Kasus,Genus],Sem_n). %np mit relationalem Nomen (hat einen durchmesser von Xkm) %Semantik (Beispiel) : lambda(lambda(Z,X),durchmesser(Z,km(200))))) np_pr(Kasus,Sem_np) --> det([Kasus,Genus],indef,_), n_rel([Kasus,Genus],lambda(Sem_mass,Sem_np)), [von], massangabe(Sem_mass). %nur in Frage zulaessig: unvollstaendige NP mit N_rel np_pr(Kasus,lambda(W,frage(V,Sem_n))) --> det([Kasus,Genus],frage,_), %Artikel-Semantik wird ignoriert n_rel([Kasus,Genus],lambda(V,lambda(W,Sem_n))). %Der Durchmesser von Jupiter np_pr1(Kasus,lambda(X,Sem_npr)) --> det_def([Kasus,Genus]), n_rel([Kasus,Genus],lambda(X,Sem_nr)), [von], np(dat,lambda(Sem_nr,Sem_npr)). /****************** Lexikon ******************/ eigenname(herschel) --> [herschel]. eigenname(lassell) --> [lassell]. eigenname(galle) --> [galle]. eigenname(tombaugh) --> [tombaugh]. eigenname(uranus) --> [uranus]. eigenname(neptun) --> [neptun]. eigenname(pluto) --> [pluto]. eigenname(ariel) --> [ariel]. eigenname(umbriel) --> [umbriel]. eigenname(triton) --> [triton]. eigenname(charon) --> [charon]. eigenname(sonne) --> [die,sonne]. fragepronomen(nom) --> [wer]. fragepronomen(akk) --> [wen]. intransitives_verb(lambda(X,lachen(X))) --> [lacht]. transitives_verb(lambda(Y,lambda(X,entdecken(Y,X)))) --> [entdeckt]. transitives_verb(lambda(Y,lambda(X,entdecken(Y,X)))) --> [entdeckte]. transitives_verb(lambda(Y,lambda(X,umkreisen(Y,X)))) --> [umkreist]. stuetzverb --> [hat]. det(Synm,indef,lambda(lambda(Z,X),lambda(lambda(Z,Y),ex(Z,und(X,Y))))) --> det_indef(Synm). det(Synm,frage,lambda(lambda(Z,X),lambda(lambda(Z,Y),frage(Z,und(X,Y))))) --> det_frage(Synm). det_indef([_,neut]) --> [ein]. det_indef([nom,mask]) -->[ein]. det_indef([akk,mask]) -->[einen]. det_def([nom,mask]) -->[der]. det_frage([_,neut]) -->[welches]. det_frage([nom,mask]) -->[welcher]. det_frage([akk,mask]) -->[welchen]. n([_,neut],lambda(X,gestirn(X))) --> [gestirn]. n([nom,neut],lambda(X,planet(X))) --> [planet]. n([_,neut],lambda(X,mond(X))) --> [mond]. n([nom,mask],lambda(X,astronom(X))) --> [astronom]. n([akk,mask],lambda(X,astronom(X))) --> [astronomen]. %relationales nomen: semantik wie transitives Verb n_rel([nom,mask],lambda(Y,lambda(X,durchmesser(X,Y)))) --> [durchmesser]. n_rel([akk,mask],lambda(Y,lambda(X,durchmesser(X,Y)))) --> [durchmesser]. %Wenn Variable massangabe(km(1)) --> [1,km]. %N ist keine Variable massangabe(km(N)) --> [N,km], {number(N)}. antwortpartikel(wahr) --> [ja]. antwortpartikel(falsch) --> [nein]. /*************************/ /*Verkleinerte Datenbasis*/ /*************************/ db(sonne,sonne,1392000,anon,nil). db(uranus,planet,51800,herschel,sonne). db(neptun,planet,49500,galle,sonne). db(pluto,planet,3000,tombaugh,sonne). %Auswahl: Zwei Monde von Uranus db(ariel,mond,1158,lassell,uranus). db(umbriel,mond,1170,lassell,uranus). %Auswahl: ein Mond von Neptun db(triton,mond,2700,lassell,neptun). %Pluto hat einen Mond. db(charon,mond,1172,christy,pluto). /**************** Hilfsprädikate Funktionieren nicht unbedingt in allen Prolog-Implementationen; getestet für SWI-Prolog unter Windows ****************/ antwortliste_zu_string(L,S) :- antwortliste_zu_string1(L,S1), string_to_list(S,S1). antwortliste_zu_string1([],[]). antwortliste_zu_string1([X],String) :- !, satz_zu_atomliste(String,X). antwortliste_zu_string1([Liste1|R],String) :- satz_zu_atomliste(Satz,Liste1), antwortliste_zu_string1(R,RestString), append(Satz,[44,32|RestString],String). satz_zu_atomliste(Satz,Liste) :- nonvar(Satz), split(Satz,Satz1), stringliste_zu_atomliste(Satz1,Liste). satz_zu_atomliste(Satz,Liste) :- nonvar(Liste), mache_string(Liste,Satz). stringliste_zu_atomliste([],[]). stringliste_zu_atomliste([K|R],[K1|R1]) :- string_to_atom(K,K1),!, %vordefiniert stringliste_zu_atomliste(R,R1). split([],[]) :- !. split(L1,[L2|R2]) :- bis_erster_blank_oder_ende(L1,L2,R3),!, split(R3,R2). bis_erster_blank_oder_ende([],[],[]). bis_erster_blank_oder_ende([32|R],[],R) :- !. bis_erster_blank_oder_ende([K|R],[K1|R1],R2) :- upper_lower_conversion(K,K1), bis_erster_blank_oder_ende(R,R1,R2). %Produziere einen String (einen Satz) aus einer Liste von Atomen mache_string([],[]). mache_string([Atom],String) :- !, string_to_atom(String1,Atom), string_to_list(String1,String). mache_string([Atom|R],String) :- string_to_atom(StringStart1,Atom), string_to_list(StringStart1,StringStart), %vordefiniert mache_string(R,StringRest), append(StringStart,[32|StringRest],String). upper_lower_conversion(C1,C2) :- nonvar(C1), C1 >= 65, C1 =< 90, !, C2 is C1+32. upper_lower_conversion(C,C). uniq([],[]). uniq([K|R],R1) :- member(K,R),!, uniq(R,R1). uniq([K|R],[K|R1]) :- uniq(R,R1).