Iedere taal en dus ook iedere programmeertaal heeft regels waaraan je moet voldoen om je op de juiste manier uit te drukken. In het vorige hoofdstuk heb je gezien dat een feit op een bepaalde manier moest worden ingevoerd (b.v. persoon(karin,vrouw).). Dat we in deze feiten geen hoofdletter gebruikten. Dat we bij een vraag met meerdere antwoorden wel een hoofdletter gebruikten (b.v. ?- persoon(Naam,man).). In dit hoofdstuk behandelen we een deel van de afspraken in de taal Prolog. De voorbeelden die gebruikt worden kun je weer naspelen door SWISH te openen en de familie kennisbank uit de vorige les weer in een Program te plaatsen.
De termen karin, vrouw en persoon zijn voorbeelden van atomen in Prolog. Een atoom (atom) (uit het grieks atomos, ondeelbaar) is een unieke reeks tekens die een enkele betekenis representeert. Er is in één programma in Prolog maar één betekenis voor een atoom. Het atoom vrouw kan dus niet meerdere dingen betekenen. Er is dus ook maar één karin. Het voorbeeld in het vorige hoofdstuk is in de huidige vorm dus ook niet geschikt voor een echte stamboom. Wat als er meerdere vrouwen Karin heten. Dit probleem is er trouwens ook in een papieren versie van een familie kennisbank. Er zal meer informatie moeten zijn om onderscheid te maken tussen verschillende personen, maar dat doen we hier nog even niet.
Atomen kunnen meerdere vormen hebben:
Een variabele (variable) in Prolog is een term die van alles en nog wat kan betekenen en ook binnen een programma van waarde kan veranderen (zoals het geval is in alle programmeertalen). In Prolog onderscheiden we twee soorten variabelen:
Een samengestelde term is opgebouwd uit een zogenoemde functor (een Prolog atoom)
en een aantal argumenten tussen ronde haakjes achter de functor. De argumenten kunnen getallen, atomen, variabelen of andere samengestelde termen zijn. Er mag geen spatie zijn tussen de
functor en het eerste haakje. Voorbeelden:
persoon(karin,vrouw), kindVan(karin,Ouder), 'Albert Einstein'(wet), f( g(X,_),7 ), ... .
De Lijst (List) in Prolog slaan we nog even over. In een van de volgende lessen gaan we daar verder op in. We geven hier slechts een voorbeeld: [de, kerstbal, hangt, in, de, boom] is een lijst met woorden opgesteld toen de editor met dit hoofdstuk in de kerstvakantie bezig was.
Het hart van de taal Prolog wordt gevormd door een database waarin feiten (facts) en clausules (=regels =clauses) worden opgeslagen. Ieder feit en iedere regel heeft een ariteit, het aantal argumenten dat de regel vereist. Iedere feit is een samengestelde term afgesloten door een punt.
Voorbeeld, een aantal feiten (facts) met ariteit 2 die we in het vorige hoofdstuk hebben gezien:
persoon(geert,man). persoon(bert,man). persoon(madelief,vrouw). kindVan(geert,harry). kindVan(geert,louise). kindVan(bert,patrick).
Andere voorbeelden van feiten zijn:
aarde. % Een feit met ariteit 0, aarde is een atoom kapper(karel). % Een samengesteld feit met ariteit 1, functor kapper, kapper en karel zijn atomen 'Keizer'(karel). % Een samengesteld feit met ariteit 1, functor 'Keizer', 'Keizer' en karel zijn atomen speler(maria, linksbenig , '25-12-2000'). % Een samengesteld feit met ariteit 3, functor speler, % speler, maria, linksbenig en '25-12-2000' zijn atomen
Merk op dat alle termen in de feiten atomen zijn. Je kunt dit testen door bijvoorbeeld de vraag ?- atom(kapper). te stellen. Let ook op het %-teken. Achter dit teken kun je commentaar in prolog schrijven.
Een clausule (regel,clause) brengt verband aan tussen twee of meer feiten.
Bijvoorbeeld: X is een vader van Y als (if, operator :- ) X een man is ( persoon(X,man) ) en
(and, operator , ) Y een kind van X is (kindVan(Y,X)).
Nog een voorbeeld: X is een zus van Y als
X een vrouw is ( persoon(X,vrouw) ) en
X een kind van ouder Z (kindVan(X,Z)) en Y een kind van de zelfde
ouder Z (kindVan(Y,Z)) en Z een moeder is persoon(Z,vrouw)
X en Y niet de zelfde persoon zijn ( X \= Y).
isVaderVan(X, Y):- persoon(X,man), kindVan(Y,X). isZusVan(X, Y):- persoon(X,vrouw), kindVan(X,Z), kindVan(Y,Z), persoon(Z,vrouw), X \= Y.
Het verschil tussen feiten en regels (facts en clauses) is de ':-' operator (=if, = als), de nek (neck), die de kop
(head) van de regel scheidt van de definitie (de body). In de body staan de eisen waaraan
moet worden voldaan wil de regel waar zijn.
Een feit (fact) is dus een bijzondere regel (clause) die altijd waar is.
Niet nodig om Prolog te leren, maar wel nuttig om te weten omdat het de theoretische basis vormt voor Prolog, zijn de volgende verwijzingen naar de propositielogica. De regels van de vorm hierboven noemt men in de logica Horn-clausules. In een Horn-clausule zijn er een aantal eisen (b.v. p, q, en r) en een doel (b.v. d). Als aan alle eisen wordt voldaan (p, q en r zijn waar) dan kan als conclusie worden getrokken dat het doel waar is. Ofwel: p ∧ q ∧ r → d. Dit is een speciaal geval van modus ponens in de logica: als P waar is dan geldt Q : P → Q (v.b. als het hier regent (P), dan wordt hier de straat nat (Q)).
Een feit (fact) is dus een bijzondere regel (clause) die altijd waar is.
Om de leesbaarheid van Prolog code te verhogen wordt ten zeerste aangeraden om samengestelde regels met meerdere termen in de body op te schrijven zoals is gedaan in de bovenstaande voorbeelden. Iedere eis waaraan moet worden voldaan komt op een nieuwe regel en er wordt ingesprongen.
Een programma is een verzameling feiten voorzien van regels. Het aanbieden van feiten en regels in een programma moet op een afgesproken manier gebeuren. Feiten en regels in de database hebben een vaste volgorde. Als meerdere clauses met dezelfde naam en ariteit (hetzelfde aantal argumenten) voorkomen worden ze geëvalueerd (uitgerekend) in de volgorde waarin ze in de broncode vermeld worden. Clauses met dezelfde naam en ariteit worden gegroepeerd om hun samenhang te benadrukken en worden als een samenhangend geheel gezien. Als een regel een waarheid oplevert dan is aan een eis voldaan. Levert een regel een onwaarheid dan wordt de volgende regel met de zelfde naam toegepast. Moderne Prolog compilers zullen een foutmelding genereren als de regels niet zijn gegroepeerd.
isBroerVan(X, Y):- persoon(X,man), kindVan(X,Z), kindVan(Y,Z), persoon(Z,vrouw), X \= Y. isBroerOfZusVan(X, Y):- isZusVan(X, Y), write(X), write(" is een zus van "), write(Y),nl. isBroerOfZusVan(X, Y):- isBroerVan(X, Y), write(X), write(" is een broer van "), write(Y),nl.
isMoederVan(X, Y):- persoon(X,vrouw), kindVan(Y,X).
isNakomelingVan(X, Y):- kindVan(X,Y). isNakomelingVan(X, Y):- kindVan(Z,Y), isNakomelingVan(X, Z).
[trace] ?- isNakomelingVan(geert,harry). Call: (8) isNakomelingVan(geert, harry) ? creep Call: (9) kindVan(geert, harry) ? creep Exit: (9) kindVan(geert, harry) ? creep Exit: (8) isNakomelingVan(geert, harry) ? creep true .
[trace] ?- isNakomelingVan(karin,truus). Call: (8) isNakomelingVan(karin, truus) ? creep Call: (9) kindVan(karin, truus) ? creep Fail: (9) kindVan(karin, truus) ? creep Redo: (8) isNakomelingVan(karin, truus) ? creep Call: (9) kindVan(_5530, truus) ? creep Exit: (9) kindVan(sylvia, truus) ? creep Call: (9) isNakomelingVan(karin, sylvia) ? creep Call: (10) kindVan(karin, sylvia) ? creep Exit: (10) kindVan(karin, sylvia) ? creep Exit: (9) isNakomelingVan(karin, sylvia) ? creep Exit: (8) isNakomelingVan(karin, truus) ? creep true .
groterDan(vinvis,olifant). groterDan(olifant,paard). groterDan(paard,ezel). groterDan(ezel,hond). groterDan(ezel,aap). isGroterDan(X,Y):- % versie 1 groterDan(X,Y). isGroterDan(X,Y):- % versie 2 groterDan(X,Z), isGroterDan(Z,Y).
Call:groterDan(vinvis, aap) % versie 1 Call:groterDan(vinvis, _9190) % versie 2 Call:isGroterDan(olifant, aap) % versie 2 Call:groterDan(olifant, aap) % versie 1 Call:groterDan(olifant, _9190) % versie 2 Call:isGroterDan(paard, aap) % versie 2 Call:groterDan(paard, aap) % versie 1 Call:groterDan(paard, _9190) % versie 2 Call:isGroterDan(ezel, aap) % versie 2 Call:groterDan(ezel, aap) % versie 1