Material 2020 för
Logikprogrammering
del av DD1351 Logik för dataloger
Thomas Sjöland sjoland@kth.se
SCS, Software and Computer Systems division CS – Computer Science department
EECS - School of Electrical Engineering and Computer Science KTH, The Royal Institute of Technology
Funktioner och relationer Deklarativ programmering
Bakgrund
Binär relation
Funktion
Om p är en binär relation så kan vi skriva:
(a, b) Î p p(a, b)
relation predikat För relationer med fler argument
(a, b, c, d, e) Î p p(a, b, c, d, e) relation predikat
Relationer
Om f är en funktion så kan vi skriva:
f(a) = b (a, b) Î f f(a, b)
funktion relation predikat Relationer är mer generella:
inte varje relation är en funktion.
Exempel: ”Fadern till a är b” kan uppfattas:
o som funktion: far(a) = b o som relation: far(a, b)
Däremot är ”b är dotter till a" en relation men inte självklart en funktion!
Funktioner
• Programmet är en beskrivning av problemdomänen
• Funktionell programmering
• Ocaml, Haskell, Scheme
• Relationell programmering
• Databasspråk (SQL), Logikprogrammering (Prolog)
• Exekveringar resulterar från frågor (queries)
• Exakta ordningen kan ändras (tillåter parallellism)
• Funktionell programmering beräknar ett värde
• Logikprogrammering konstruerar ett eller flera bevis
Deklarativ programmering
• Predikat beskriver relationer mellan objekt
• Domänen beskrivs som en sammansättning av
predikatlogiska formler i så kallad Horn-klausul form
• Frågor besvaras med en sök-algoritm som kallas resolution
• Exekveringen motsvarar sökningen efter ett bevis
• Resultatet är bindningar för logiska variabler
Logikprogrammering
• Se kurswebbsidorna: Före kursstart, Kursmaterial
• Kursbok: Learn Prolog Now!
P. Blackburn, J. Bos, och K. Striegnitz
Free online version: www.learnprolognow.org
• SWI-Prolog manual
• Föreläsningsanteckningar
• Prolog-l: *.pl
Delkurslitteratur och -material
• Logisk versus procedurell läsning
• Kontrollflöde: Unifiering, Backtracking, Snitt, Negation
• Data och rekursion
• Sammansatta data med syntaxstöd: listor
• Sammansatta data, generellt: träd
• Programmeringtekniker med Prolog
• Generera-och-testa
Delkursinnehåll
Efter godkänd kurs ska studenten kunna:
specificera allmänna egenskaper hos matematisk-datalogiska strukturer och bevisa dessa med hjälp av naturlig deduktion i satslogik och predikatlogik,
specificera induktiva definitioner hos datastrukturer och bevisa dessa med strukturell induktion, specificera och bevisa systemegenskaper med hjälp av temporal logik,
specificera och bevisa programegenskaper med hjälp av Hoarelogik,
tillämpa metoder för automatisk deduktion och utföra enkla bevis med modellprövning,
tillämpa och förklara grundläggande begrepp inom logikprogrammering:
unifiering, backtracking, snitt, negering och olika programmeringstekniker som t.ex. generate-test
i syfte att
behärska de bevistekniker som behövs i kommande kurser i utbildningen.
För högre betyg ska studenten dessutom kunna:
argumentera för korrektheten hos en viss bevisteknik: sundhet och fullständighet, argumentera för bevisteknikers lämplighet till automatisk deduktion: avgörbarhet.
Lärandemål DD1351
Logik som programmeringsspråk - Prolog Likhet för termer - Unifiering
Definition av relationer Rekursion
Backtracking
DD1351 –
Logikprogrammering
föreläsning 1(4) 9 sep 2020
Ämnen
• Data och variabler i logikprogram
• Unifiering
• Satser: fakta och regler
• Logisk versus procedurell läsning
• Backtracking
• Rekursion
Läsmaterial
• Boken: kap. 1-3
• Föreläsningsanteckningar
Idag
Data (objekt) i logikprogram
Program uttrycker sanningar som ett antal predikat (relationer) över en värld av data (objekt)
Konstanter
Individkonstanter Tal
Sammansatta termer, strukturer (normalt finita)
Träd
Specialfall (med syntaxstöd): Listor
Objekt i logikprogram
Individkonstanter
a ’B’ foo 4711 -37 34.5
Funktorer
strukturnamn för noder i träd
samma syntax som icke-numeriska individkonstanter
Syntaxexampel. en struktur (ett träd):
term(a,b,-4711,other(b,a))
Logiska variabler - Syntax
Syntax: börjar med stor bokstav (eller '_’) X Y Z Foo _Bar _
Variabler kan finnas överallt där konstanter eller strukturer förekommer.
Motsvarar termer, logiska objekt
"anonym variabel" eller "void” _
foo(X,Y,Z)
matchar termer med funktorn foo och argumenten termer som matchar X, Y, Z.
foo(T,a,T)
matchar termer med funktorn foo och argument som matchar T, a, och en term som matchar
samma T i position 3 bar(_,_,_)
matchar varje term med funktorn bar och tre argument, inte nödvändigtvis samma
Logiska variabler - Syntax
Termer
Givet en mängd variabler V, en mängd konstanter C,
och en mängd funktionssymboler F t är en term omm
t Î V eller t Î C
eller t = f(t
1,…t
n) där f Î F och t
iär en term
Två termer kan unifieras om det finns en substitution (med termer) för variablerna i termerna, så att båda termerna blir syntaktiskt identiska.
Exempel:
• X och agnes unifierar med substitutionen X = agnes
• far(X, petter) och far(agnes, petter) unifierar med substitutionen X = agnes
• far(agnes, Y) och far(agnes, petter) unifierar med substitutionen Y = petter
• far(X, petter) och far(agnes, Y) unifierar med substitutionen X = agnes, Y = petter
• Kan far(X, petter) och far(agnes, X) unifiera?
Term-unifiering
X=t(V1, f(3,g(4, V2)), V3), Y=t(h(V2), f(3,g(4, 5)), V1), X=Y
--- [unifiering]
X=t(h(5), f(3,g(4, 5)), h(5)) Y=t(h(5), f(3,g(4, 5)), h(5)) V1=h(5)
V2=5 V3=h(5)
Unifiera nästlade termer
Predikatlogikens syntax har såväl
predikatsymboler som funktionssymboler, tagna från olika syntaktiska mängder.
I Prolog däremot finns det ingen syntaktisk
skillnad mellan dem, och båda representeras som atomer.
Det är kontexten som bestämmer hur symbolerna tolkas.
Prolog-termer
I satsen:
a(b, c(X)).
är a en 2-ställig predikatsymbol, c en 1-ställig funktionssymbol,
b en konstant (dvs en 0-ställig funktionssymbol), och X en variabel.
OBS Funktionssymboler evalueras inte.
Prolog-termer
Relationer kan deklareras explicit, varje tupel för sig själv (uppfattas som en
konjunktion av klausuler).
Sådana definitioner kallas för fakta:
far(agnes, petter).
far(per, petter).
far(anton, per).
mor(agnes, annika).
mor(per, annika).
mor(kia, agnes).
mor(anton, agnes).
Prologsatser: fakta
Nu när vi har skapat en relationell databas, vill vi kunna ställa frågor (queries) till den.
?- far(agnes, per).
?- far(anton, per).
?- far(monika, per).
Notera att Prolog inte vet vad vi menar med alla symboler. Systemet resonerar helt symboliskt, utan tolkning!
Prolog vet inte om far(a, b) betyder ”far till a är b", eller ”a är far till b", eller t.o.m. att ”himlen har färgen a och det är b stycken moln på den".
Frågeformler, queries
Vi kan också ställa frågor som involverar variabler:
?- far(agnes, X).
Vem är far till agnes?
Eller, kan du hitta ett X som gör frågan sann?
?- far(X, petter).
Vems far är petter?
Flera svar möjliga!
Fås ett i taget med ”n” eller ";"
?- far(X, Y).
Frågor med variabler
Relationer definieras med regler:
farmor(X, Y) :- far(X, Z), mor(Z, Y).
Läs: X farmor är Y
om far till X är (någon) Z och mor till Z är Y.
Vi kan observera att:
Ø":-" läses som "om", dvs implikation ”<-" i omvänd riktning
Ø"," här läses som "och", dvs konjunktion "^"
Øvariablerna är (implicit) universellt kvantifierade Sådana formler (satser) kallas för Horn-klausuler
Prologsatser: regler
kon jun ktio n
OBS att
far(anton, per).
kan skrivas
far(anton, per) :- true.
eller
far(X, Y) :- X= anton, Y= per.
så vi behöver bara en form av sats för definitioner Head :- Body
Prologsatser: regler
Disjunktiva regler kan anges som separata klausuler:
foralder(X, Y) :- far(X, Y).
foralder(X, Y) :- mor(X, Y).
eller ekvivalent som
foralder(X, Y) :- far(X, Y) ; mor(X, Y).
Läs:
en förälder till X är Y
om far till X är Y eller mor till X är Y.
Följer logiska ekvivalensen:
p v q -> r ó (p -> r ) ^ (q -> r )
Prologsatser: regler
disj unk tion
• Problemdomän beskrivs med relationer
• Fakta och regler
• Data som termer
Prolog söker svar på frågor om domänen
Den deklarativa aspekten
• Unifiering för att göra termer lika
• Sökning med backtracking
• Typisk programstruktur: generera-och-testa
• Snitt
• Att ta bort oönskade beräkningssteg eller svar
Prolog söker svar genom att bygga ett enkelt logiskt bevis för ett påstående med användning av programmets satser.
Den procedurella aspekten
kontrollflödet
Logisk läsning
Ø som en predikatlogisk formel, deklarativt
Procedurell läsning
Ø proceduren som Prolog följer för att hitta ett bevis Ø Prolog läser klausulerna uppifrån och ned, och
kroppens literaler från vänster till höger
Ø Prolog försöker instansiera variablerna med termer så att målet blir sant: unifiering
Ø om detta misslyckas går Prolog till föregående literal inom klausulens kropp om en sådan finns, och annars till nästa klausul i programmet: backtracking
Logisk versus procedurell läsning
Bevisteori:
Exekvering är att söka efter ett bevis eller misslyckande. Detta genererar ett eller-träd
begränsningar på variablernas värden visas som bindningar hos
variablerna
Sökträd och bevisträd
bevis
Sökträd Bevisträd
lösning
Hur Prolog exekverar
En fråga ?- p(Args).
bevisas med resolution
- leta efter alla klausuler som definierar p
- välj den första, spara de kvarvarande som alternativ
- matcha argumenten Args med termerna i klausulens huvud, skapa nödvändiga variabel- bindningar med unifiering
- om unifieringen misslyckas, prova nästa alternativa klausul
- bevisa annars målen i kroppen från vänster till
höger; om detta misslyckas, prova alternativ klausul
- om alla bevis är klara, presentera bindningarna,
annars är resultatet ” no ” eller ” fail ”.
Mål: farmor(anton, X).
Skapar regelinstans:
farmor(X1, Y1) :- far(X1, Z1), mor(Z1, Y1).
unifierar X1=anton, Y1=X
Delmål: far(anton, Z1).
unifierar Z1=per
Delmål: mor(per, X).
unifierar X=annika
Svar: X=annika
Kontrollflödet i Prolog: Exempel 1
Ø Mål: foralder(kia, X).
Ø Skapar regelinstans:
foralder(X1, Y1) :- far(X1, Y1).
unifierar X1=kia, Y1=X Ø Delmål: far(kia, X).
Kan inte unifieras. Backtrackar till nästa regel.
Ø Skapar regelinstans:
foralder(X2, Y2) :- mor(X2, Y2).
unifierar X2=kia, Y2=X
Ø Delmål: mor(kia, X).
unifierar X=agnes
Ø Svar: X=agnes
Kontrollflödet i Prolog: Exempel 2
Regler kan vara rekursiva, dvs referera till sig själv:
forfader(X, Y) :- foralder(X, Y).
forfader(X, Y) :- foralder(X, Z), forfader(Z, Y).
Betrakta frågan:
?- forfader(kia, X).
Vad blir kontrollflödet till första svaret?
Och till andra svaret? (';' leder till backtracking!)
Vad skulle hända om vi vänder om ordningen på de två reglerna?
Och på de två konjunkterna i andra regeln?
Rekursion
För att få en bättre förståelse av
kontrollflödet i Prolog och trace-funktionen, läs också om lådmodellen: se handouts.
Kontrollflödet i Prolog: Lådmodellen
Likhet = betyder ”unifierbara”, inte "samma värde"!
Speciella predikatet is utvärderar andra termen och unifierar med första. Obs: andra termen måste vara tillräckligt instansierad!
X is Expression
Dubbelsidig utvärdering och jämförelse: =:=.
Aritmetiska operatorer: +, -, *, /, //, mod, etc.
Olikheter: <, >, =<, >=, etc.
T.ex. X<35, X>=13. ” X finns i intervallet [13..35[”
Aritmetik i Prolog
Två nödvändiga predikat:
Øread(t): läs en term fram till nästa punkt och unifiera den med termen t;
Øwrite(t): skriv ut termen t till terminalen.
Vad händer vid frågan:
?- read(X+Y), write(Y-3).
I/O i Prolog
Syntaktiskt stöd
Med op/3 kan egenskaper (prioritet, prefix, infix, postfix, associativitet) hos operatorer defineras och därefter
användas. (see manualen för detaljer)
Predikat som definieras av användaren skrivs med samma syntax som datastrukturer, till exempel
:- op(950, xfy, [in]).
foo(Y) :- X in Y, baz(Y in U,Z).
Material 2020 för
Logikprogrammering
del av DD1351 Logik för dataloger
Thomas Sjöland sjoland@kth.se
SCS, Software and Computer Systems division CS – Computer Science department
EECS - School of Electrical Engineering and Computer Science KTH, The Royal Institute of Technology
Lite om databasoperationer Induktiv definition av data:
Sammansatta termer
Listor (inbyggt syntaxstöd)
DD1351 –
Logikprogrammering
föreläsning 2(4) 14 sep 2020
Listor
• Strukturell induktion över listor
• Kontrollflöde
• Generera och testa
• Strängar
Läsmaterial
• Boken: kap. 4, 6
• Prolog-l: list.pl
• Handouts: Föreläsningsanteckningar
Idag
LP och relationsdatabaser
Union:
r_union_s(X1,…,Xn) :- r(X1,…,Xn).
r_union_s(X1,…,Xn) :- s(X1,…,Xn).
Cartesian product:
r_X_s(X1,…,Xn,Y1,…,Ym) :- r(X1,…,Xn), s(Y1,…,Ym).
Projection:
r1(X1,X3) :- r(X1,X2,X3).
LP och relationsdatabaser
Selection:
r1(X1,X2,X3) :- r(X1,X2,X3), X2\=X3.
Natural join:
r_join_s(X1,…,Xn, X, Y1,…,Ym) :- r(X1,…,Xn, X), s(X,Y1,…,Ym).
Intersection:
r_meets_s(X1,…,Xn) :-
r(X1,…,Xn), s(X1,…,Xn).
LP och databaser, sammanfattning
LP kan användas som ett uniformt språk för att representera databaser, t.ex. datastrukturer and frågor kan skrivas med ett språk
LP utvidgar traditionella databaser genom att
det har rekursiva regler och strukturerad data
Exempel: strukturerad data
Använd sammansatta termer för beskrivning av personer.
parent(erik, jonas).
parent(erik, eva).
parent(lena, jonas).
male(person(erik,50,180cm)).
male(person(jonas,25,_)).
father(Dad,Child) :- Dad = person(DadName,_,_),
Child = person(ChildName,_,_), parent(DadName, ChildName), male(Dad).
?-father(person(_,50,_), person(X,_,_)).
X=jonas (lösning #2: X = eva)
Fråga: Hur fungerar unifieringsalgoritmen här? Beskriv kontrollflödet!
%Kommentarkonvention
% +X betyder X är avsedd som ”input”
% -X betyder X är avsedd som ”output”
% ?X betyder X kan var input eller output
%
% foo(+X,?Y,-Z)
%
% OBS en term kan vara partiellt instantierad, samtidigt ”input” och ”output”
Konventioner om input och output
Att konstruera rekursiva definitioner
Tänk på deklarativa meningen hos den rekursiva relationen (en datatypsdefinition)
Skriv ner rekursiva klausuler och basklausul(er) Ladda in i prologsystemets interpretator
Kör enkla exempel - testa olika mål Kontrollera vad som händer
- Får du det resultat du väntar dig?
Att konstruera rekursiva definitioner
Typiska fel:
Saknat eller felaktigt definierat basfall Fel i datastrukturernas representation Fel ställighet (arity) hos strukturer
Fel ställighet (arity) hos predikat (relationer) Sammanblandning av ett element och en lista Permuterade argument
Felstavningar. Stora och små bokstäver
Samma variabler används för olika saker
Naturliga tal
Unär syntax
Till exempel,
0 - betyder noll
s(0) - betyder ett
s(…s(s(0))…) ... - betyder talet n
Att definiera de naturliga talen
natural_number(0).
natural_number(s(X)) :-
natural_number(X).
Naturliga tal
plus
plus(0, X, X) :- natural_number(X).
plus(s(X), Y, s(Z)) :- plus(X, Y, Z).
?- plus(s(0),0,s(0)). – kontrollerar om 1+0=1 Yes.
?- plus(X,s(0),s(s(0))). - kontrollerar om X+1=2, (beräkna X=2-1) X=s(0).
?- plus(X, Y, s(s(0))). - kontrollerar om X+Y=2, (t.ex. generera alla par av naturliga tal, vilkas summa är två)
X=0, Y=s(s(0));
X=s(0), Y=s(0);
X=s(s(0)), Y=0.
Naturliga tal
Mindre än eller lika
le(0, X) :- natural_number(X).
le(s(X), s(Z)) :- le(X, Z).
multiplikation
times(0, X, 0) :- natural_number(X).
times(s(X), Y, Z) :-
plus(Y, Z1, Z), times(X, Y, Z1).
Kontrollera hur substitutionerna I unifieringen fungerar!
Binära träd
tree(5,tree(8,void,void),tree(9,void,tree(3,void,void)))
8
5
3 9
void void void
void void
Binära träd (mera tydligt)
tree(5,tree(8,void,void),tree(9,void,tree(3,void,void)))
tree
tree
tree tree
void void void
void void
5
3
8 9
Binära träd
Syntax (inte inbyggt, du definierar dina egna sammansatta termer)
Till exempel,
void - betyder tomma trädet
tree(Element, Left, Right)
- betyder ett träd, där Element är roten och Left, Right är delträd tree(5,tree(8,void,void),tree(9,void,tree(3,void,void)))
Att definiera ett träd
binary_tree(void).
binary_tree(tree(Element, Left, Right)) :-
binary_tree(Left), binary_tree(Right).
Binära träd
medlemskap
tree_member(X,tree(X,_,_)).
tree_member(X,tree(Y,Left ,_)):- tree_member(X,Left).
tree_member(X,tree(Y,_,Right)):- tree_member(X,Right).
OBS: X kan vara samma term som Y I klausulerna 2 och 3!
Binära träd– mirror/2
Att spegla ett träd
mirror(void,void).
mirror(tree(Y,L1,R1),tree(Y,R2,L2)):- mirror(L1,L2),
mirror(R1,R2).
3 1
4 2
void void void
void void
3
1
4 2
void void void
void void
Listor
'.'(Head,Tail) cons cell
Head är ett element, Tail är en lista) [] tomma listan
Föredragen syntax (oftast): [Head|Tail]
enklare syntax
[a|[]] = [a] [a|[b|[c|[]]]] = [a,b,c]
. . . -
.
.
a b c
[]
[]
a
b
c
Listor
Syntax
[Head|Tail] eller '.'(Head,Tail) cons-cell Head är ett element, Tail är en lista
[] tomma listan
definiera en lista
list([]). - definera basen
list([X|Xs]) :- list(Xs). - definera rekursionen
Listor
Att kontrollera medlemskap
member(X, [X|Xs]).
member(X, [Y|Ys]) :- member(X, Ys).
?- member(a, [b,c,a,d]). - att kontrollera medlemskap
?- member(X, [b,c,a,d]). - hitta ett element i en lista
?- member(b, Z). - generera en lista som innehåller b
Rekursiv aritmetik
Jämför dessa två definitioner och förklara skillnaden:
sum([],0).
sum([H|T],S) :- sum(T,V), S is H+V.
sum0([],0).
sum0([H|T],S) :- sum0(T,V), S=H+V.
Listorna utgör en oändlig mängd av Prolog-termer.
Induktiv definition:
en lista är antingen tom [], eller en konstruktion [H|T]
av ett element H ("huvud") och en lista T ("svans").
OBS kom ihåg även denna konvention!
’.’(H,T)= [H|T]
Listor
Definition i Backus-Naur Form (BNF):
<Lst> ::= [] | [<El>|<Lst>]
där <El> är vilken som helst Prolog-term.
Därmed matchar varje lista l antingen []
eller [H|T] , och vi kan använda detta för att ta isär (destruera) listor för att definiera predikat över listor med strukturell
induktion.
Listor
Notationskonvention:
Vi skriver
[a, b, c]
istället för
[a | [b | [c | []]]],
detta gör också Prologs "pretty-print".
OBS även denna konvention!
’.’(H,T)= [H|T]
Listor
Strukturell induktion är en princip att garantera ”well- definedness”.
För induktivt definierade datatyper kan vi använda oss av strukturell induktion för att definiera predikat över dem.
Konkret, för listor:
• för tomma listan [], definiera predikatet icke-rekursivt;
• för sammansatta listan [H|T] , definiera predikatet med användning av samma predikat, men bara över svansen T.
Om man följer principen garanterar man att predikatet blir
"väldefinierat" över alla listor.
Läs också texten: "The Principle of Structural Induction" !
Strukturell induktion över listor
is_list([]).
is_list([ _ | T]) :-
is_list(T).
first_element([ E | _],E).
last_element([E],E).
last_element([ _ | T],E) :- last_element(T,E).
OBS syntaxstöd: [A] = ’.’(A,[]) = [A|[]]
Predikat över listor
Längden på en lista L är antalet element N i listan.
Definition med strukturell induktion:
• längden på tomma listan [] är 0;
• längden på sammansatta listan [H | T] är längden på svansen T plus 1.
Låt oss använda detta för att implementera listLength(L, N).
Längden på en lista
I Prolog:
listLength([], 0).
listLength([ _ | T], N) :- listLength(T, NT), N is NT + 1.
Notera att vi använder operatorn "is" istället för "=". Varför?
Notera också hur vi använder mönster-matchning i första argumentet för att åstadkomma datatyp-destruktionen som är
nödvändig för strukturella induktionen.
Kan vi vända på ordningen på de två konjunkterna?
Finns även inbyggd som length(L, N).
listLength(L, N)
Mål: listLength([a, b], N).
skapar en instans av andra regeln:
listLength([A1 | T1], N1) :- listLength(T1, NT1), N1 is NT1 + 1.
unifierar A1=a, T1=[b], N1=N
• Delmål: listLength([b], NT1).
• skapar instans av andra regeln:
• listLength([A2 | T2], N2) :- listLength(T2, NT2), N2 is NT2 + 1.
• unifierar A2=b, T2=[], N2=NT1
• Delmål : listLength([], NT2).
• matchar första regeln, unifierar NT2=0
• Delmål : NT1 is 0 + 1.
• evaluerar 0+1 till 1, unifierar NT1=1
• Delmål : N is 1 + 1.
• evaluerar 1+1 till 2, unifierar N=2 Svar: N=2
Kontrollflödet vid listLength([a, b], N).
Från KS:en HT15:
Fråga: listLength(L, 2).
- misslyckas med första regeln, därför att 0 och 2 inte kan unifieras - skapar instans av andra regeln
listLength([A1|T1], N1) :-
listLength(T1, NT1), N1 is NT1+1.
unifierar: L=[A1|T1], N1=2 -- listLength(T1, NT1).
--- lyckas med första regeln unifierar: T1=[], NT1=0 -- 2 is 0+1.
--- evaluerar 0+1 till 1
--- misslyckas, därför att 2 och 1 inte kan unifieras --- backtrackar
….. (forts på nästa bild)
Kontrollflödet vid listLength(L, 2).
Från KS:en HT15:
….
(Forts från föregående bild) -- listLength(T1, NT1).
--- skapar instans av andra regeln:
listLength([A2|T2], N2) :- listLength(T2, NT2), N2 is NT2+1.
unifierar: T1=[A2|T2], N2=NT1 ---- listLength(T2, NT2)
--- lyckas med första regeln unifierar: T2=[], NT2=0 ---- NT1 is 0+1
--- evaluerar 0+1 till 1 --- unifierar: NT1=1 -- 2 is 1+1
--- evaluerar 1+1 till 2
--- lyckas, därför att 2 kan unifieras med 2
Svar: L=[A1|[A2|[]]] som presenteras som L=[A1, A2]
Kontrollflödet vid listLength(L, 2).
(forts)
Medlemstest som ska vara sant om och bara om X finns i listan L.
in(H, [H | _]).
in(X, [_ | T]) :- in(X, T).
Strukturella induktionen är över listan L (dvs andra argumentet).
in(X, []) är alltid falskt: ingen regel för tom lista!
Finns även inbyggd som member(X, L).
Medlemskap i en lista: in(X, L)
• Mål: in(2, L), in(1, L).
• Delmål: in(2, L).
• skapar instans av första regeln:
in(H1, [H1 | A1]).
unifierar H1=2, L=[2 | A1]
• Delmål : in(1, [2 | A1]).
• skapar instans av första regeln:
in(H2, [H2 | A2]).
misslyckas med unifieringen (varför?)
• skapar instans av andra regeln:
in(X1, [A3 | T1]) :- in(X1, T1).
unifierar X1=1, A3=2, T1=A1
• Delmål : in(1, A1).
• skapar instans av första regeln:
in(H3, [H3 | A4]).
unifierar H3=1, A1=[1 | A4]
• Svar: L =[2, 1 | A4] Finns det fler svar? Hur hittas L=[1, 2] ?
Kontrollflödet vid in(2, L), in(1, L).
Ska vara sant om Z är konkateneringen av listan X med listan Y.
append([], Y, Y).
append([HX | TX], Y, [HX | TZ]) :- append(TX, Y, TZ).
Strukturella induktionen är över listan X (dvs första argumentet).
(Finns inbyggd i många prologsystem.)
Sätta samman listor: append(X, Y, Z)
Listor
Konkatenering av två listor
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append([a,b], [c,d], X). - sammansättning av två listor
. .
a b
[]
. .c d
[]
. .
a b
. .
c d
X= []
Listor
“differens” mellan två listor
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, [c,d], [a,b,c,d]).
- finner “differensen” mellan listor
. .
a b
[]
. .c d
[]
. .
a b
. .
c d
[]
Xs=
Listor
Att dela upp en lista i två delar (1)
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, Ys, [a,b,c,d]).
. .
a b
[]
. .
c d
[]
. .
a b
. .
c d
[]
Xs= Ys=
Listor
Att dela upp en lista i två delar (2)
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, Ys, [a,b,c,d]).
. .
a b
[]
. .c d
[]
. .
a b
. .
c d
[]
Xs= Ys=
Listor
Att dela upp en lista i två delar (3)
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, Ys, [a,b,c,d]).
. .
a b
[]
. .c d
[]
. .
a b
. .
c d
[]
Xs= Ys=
Listor
Att dela upp en lista i två delar (4)
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, Ys, [a,b,c,d]).
. .
a b
. .
c d
[]
. .
a b
. .
c d
[]
Xs= [] Ys=
Listor
Att dela upp en lista i två delar (5)
append([], Xs, Xs).
append([X|Xs], Y, [X|Zs]) :- append(Xs, Y, Zs).
?- append(Xs, Ys, [a,b,c,d]).
. .
a b
. .
c d
[]
. .
a b
. .
c d
[]
Xs= Ys= []
Listor
Att vända på en lista
reverse([], []).
reverse([H|T],R) :- reverse(T,S), append(S,[H],R).
?- reverse([a,b,c,d],R). - ger R=[d,c,b,a]
. .
a b
. .
c d
[]
. .
d c
. .
b a
R= []
Ska vara sant om listan NL är listan L med elementet X tillagt i slutet.
appendEl(X, [], [X]).
appendEl(X, [H | T], [H | Y]) :- appendEl(X, T, Y).
Strukturella induktionen är över listan L (dvs andra argumentet).
Lägg till ett element: appendEl(X, L, NL)
Ska vara sant om Y är omvända listan X.
rev([], []).
rev([H | T], Y) :-
rev(T, RT),
appendEl(H, RT, Y).
Finns även inbyggd som reverse(X, Y).
Listomvändning: rev(X, Y)
Ackumulerande parametrar
Reversering av listor
a) naive reverse (använder append i varje rekursionssteg) reverse([], []).
reverse([X|Xs], Ys) :-
reverse(Xs, Zs), append(Zs, [X], Ys).
b) reverse med ackumulator (argument 2 i hjälppredikatet) reverse(Xs, Ys) :-
reverse(Xs, [], Ys).
reverse([], Acc, Acc).
reverse([X|Xs], Acc, Ys) :-
reverse(Xs, [X|Acc], Ys).
Varför är denna teknik viktig?
Tänk på hur många steg som blir resultatet för långa listor!
Strängar
• Är symbolsekvenser.
• Representeras i Prolog internt som listor av heltal. Varje tal representerar därmed en
symbol (ASCII-koden).
• Inbyggda predikatet atom_codes(X, Y) är sant när Y är strängen (dvs heltalslistan) som motsvarar atomen X.
Strängar
Det finns många sätt att sortera listor.
Här ska vi implementera
permutationssortering, som illustration
av hur vi kan använda backtracking som en styrka för att realisera en
programmeringsteknik som kallas för generera och testa.
Listsortering
Denna programmeringsteknik använder sig av
backtracking för att successivt generera en möjlig lösning, testa om den uppfyller villkoren för att vara en korrekt
lösning, sedan backtracka och generera en möjlig lösning till, testa den, osv.
Generella strukturen ser ut så här:
problem(Problem, Solution) :-
generate(Problem, Solution), test(Solution).
För effektivitet behöver man ofta ”väva in” test i generatorn.
Generera och testa
Vi utgår från den matematiska definitionen av sortering: att sortera en lista kan definieras som att skapa (dvs beräkna) en sorterad permutation av ursprungliga listan:
permSort(X, Y) :-
permutation(X, Y), \\ generera permutation sorted(Y). \\ testa om sorterad
Programmet använder (inbyggda) predikatet
permutation(X, Y) för att generera en permutation av listan X, som sedan testas med predikatet sorted(Y)
huruvida den är sorterad eller inte.
Listsortering: permSort(+X, ?Y)
Ska vara sant om Y är en permutation av X.
Strukturella induktionen är på första listan X.
Vi utgår från följande logiska karakterisering av permutation:
Y är en permutation av X om huvudet på X (E) finns i Y, och om E tagits bort från Y, den resulterade listan är en permutation av svansen på X.
permutation([], []).
permutation([E | X], Y) :- permutation(X, Y1),
append(Y2, Y3, Y1), % används för att dela upp Y1 append(Y2, [E | Y3], Y).
Del 1: permutation(+X, ?Y)
Definieras här bara för listor av tal!
sorted([]).
sorted([X]).
sorted([X, Y | L]) :-
X =< Y,
sorted([Y | L]).
Del 2: sorted(X)
permutation(Xs, [Z|Zs]) :- select(Z, Xs, Ys), permutation(Ys, Zs).
permutation([], []).
select(X, [X|Xs], Xs).
select(X, [Y|Ys], [Y|Zs]) :- select(X, Ys, Zs).
Mer elegant permute
Material 2020 för
Logikprogrammering
del av DD1351 Logik för dataloger
Thomas Sjöland sjoland@kth.se
SCS, Software and Computer Systems division CS – Computer Science department
EECS - School of Electrical Engineering and Computer Science KTH, The Royal Institute of Technology
Definitioner över träd (sammansatta termer)
DD1351 –
Logikprogrammering
föreläsning 3(4) 16 sep 2020
Summera tal i en lista Summera tal i ett träd Definitioner över träd Binära träd utan data Binära träd med data
ØProblemdomänbeskrivning
Läsmaterial
ØProlog-l: tree.pl
ØHandouts: Föreläsningsanteckningar
Idag
Summera talen i en lista
Example
Definiera ett predikat som summerar talen i en lista.
a) rekursion
sumlist([], 0).
sumlist([I|Is], Sum) :-
sumlist(Is, Sum1), Sum is Sum1 + I.
b) iteration (med ackumulator)
sumlist(List, Sum) :- sumlist(List, 0, Sum).
sumlist([], Sum, Sum).
sumlist([I|Is], Sum1, Sum) :- Sum2 is Sum1 + I,
sumlist(Is, Sum2, Sum).
Hur mycket minne behövs?
Algoritmer över träd
Definiera ett predikat för att beräkna summan av talen i ett träd Här är en rättfram lösning:
sumtree(void,0).
sumtree(tree(E,L,R),S) :- sumtree(L,S1),
sumtree(R,S2),
S is E+S1+S2.
3
1
4 2
void void void
void void
Algoritmer över träd
Definiera ett predikat för att beräkna summan av element i ett träd - Gå igenom trädet till exempel vänster-till-höger
- För varje nod: lägg till talet till en ackumulator - Presentera slutvärdet som resultatet!
Here is the efficient solution:
sumtree(T,S) :-
sumtree(T,0,A), S is A.
sumtree(void,A,A).
sumtree(tree(E,L,R),A,S) :- sumtree(L,E+A,S1),
sumtree(R,S1,S).
3 1
4 2
void void void
void void
Definiera ett predikat för att platta ut en nästlad lista!
- T. ex. flatten([a,[b,c],d],L) skall generera listan L=[a,b,c,d]
- Tips: Kan du använda en ackumulator?
Nästlade listor
. .
a
.
d
[]
.
b
.
c
[]
.
a
.
d
[]
.
b
.
c
Definiera ett predikat för att slå samman en lista av listor!
- T.ex. listan [[1,2],[3],[4,5],[6,7,8]]
- skall generera listan [1,2,3,4,5,6,7,8]
Frågan: Hur effektivt kan du göra detta predikat?
Att lägga samman många listor
Binära träd utan data utgör en oändlig mängd av Prolog-termer:
Ett binärt träd utan data är antingen ett löv, leaf, eller ett sammansatt träd, branch(t1, t2) , som består av ett vänster delträd t1 och ett höger
delträd t2.
Induktiv definition (BNF)
<Tree> ::= leaf | branch(<Tree>, <Tree>) där leaf och branch kallas för konstruktorer.
Därmed matchar varje binärt träd utan data t antingen leaf eller branch(TL, TR).
Exempel på (stängda) träd-termer: leaf, branch(leaf, leaf), branch(leaf, branch(leaf, leaf)).
Träd-termer kan också innehålla variabler: branch(X, leaf).
Binära träd utan data
För att definiera ett predikat över binära träd utan data:
§ för löven leaf, definiera predikatet utan rekursion;
§ för sammansatta träden branch(TL, TR), definiera predikatet med användning av samma predikat över delträden TL och TR.
Då blir predikatet väldefinierat för alla binära träd utan data.
Strukturell induktion
Höjden på ett träd t är längden (antalet bågar) av längsta stigen från roten till något löv.
Definition med strukturell induktion?
§ höjden på ett löv leaf är 0;
§ höjden på ett sammansatt träd
branch(TL, TR) är maximum av
höjderna på delträden TL och TR plus 1.
Höjden på ett träd
max(X, Y, X) :- Y<X.
max(X, Y, Y) :- X=<Y.
height(leaf, 0).
height(branch(TL, TR), N) :- height(TL, NL),
height(TR, NR), max(NL, NR, M), N is M+1.
Höjden på ett träd: height(T, N)
Ett träd är komplett om det har alla sina löv på samma höjd.
Definition med strukturell induktion?
balanced(leaf).
balanced(branch(TL, TR)) :- balanced(TL),
balanced(TR), height(TL, N), height(TR, N).
Kompletta träd: balanced(T)
Ett binärt träd med data är antingen ett löv leaf(d) med en data-term d, eller ett sammansatt träd branch(d, t1, t2)
med
en data-term d, ett vänster delträd t1, och ett höger delträd t2.
Induktiv definition (BNF)
<Tree> ::= leaf(<Data>) |
branch (<Data>, <Tree>, <Tree> ) Därmed matchar varje binärt träd med data antingen leaf(D) eller branch(D, TL, TR).
Exempel:
branch(-3,branch(4,leaf(8),leaf(-2)),leaf(7))
Binära träd med data
Som member(X, L), fast för träd med data.
Definition med strukturell induktion
lookup(D, leaf(D)).
lookup(D, branch(D, _, _)).
lookup(D, branch(_, TL, _)) :- lookup(D, TL).
lookup(D, branch(_, _, TR)) :- lookup(D, TR).
Medlemskap i ett träd: lookup(D, T)
Beräknar summan av alla tal i trädet (antar att allt data är tal).
Definition med strukturell induktion
treesum(leaf(N), N).
treesum(branch(N, TL, TR), N1) :- treesum(TL, NL),
treesum(TR, NR), N1 is NL+NR+N.
Summering i ett träd: treesum(T, N)
Operationer
ØAtt lägga till ett element.
ØAtt ta bort ett element.
Problematiskt för binära träd!
Ofullständiga binära träd (utan data)
Øidé: tillåt också tomma träd. (skrivs nil) ØBNF-definition:
<Tree> ::= nil | leaf | branch(<Tree>, <Tree> ) Ø exempel: branch(leaf, branch(nil, leaf))
Strukturella operationer på träd
Datatypen hierarkiska mjukvarusystem kan definieras induktivt med två regler: ett mjukvarusystem är antingen en funktion (som i språket C), eller en modul bestående av mjukvarusystem.
1. Föreslå ett sätt att representera mjukvarusystem som Prolog- termer, där funktionerna representeras som atomer med samma namn som funktionen, t.ex. main, min, max.
2. Ge två exempel på termer som representerar mjukvarusystem med flera nästlade nivåer.
3. För din representation av datatypen mjukvarusystem, skriv ett predikat funcsMVS(S, FL) som är sant om och bara om FL är en lista som innehaller namnen pa alla funktioner i det hierarkiska mjukvarusystemet S (med möjlig upprepning).
Problemdomänbeskrivning:
Hierarkiska mjukvarusystem
1. Sammansättningar går enklast att representera som listor.
En möjlig BNF-grammatik vore:
<MVS> ::= func(<Name>) | mod(<MVSList>)
<MVSList> ::= [] | [<MVS>|<MVSList>]
2. Exempel-termer:
Ø func(foo)
Ø mod([func(main),
mod([func(min), func(max), func(avg)])])
Hierarkiska mjukvarusystem: Prolog-termer
3. Om vi följer strukturella induktionsprincipen för
ömsesidigt rekursiva datatyper (se texten "The Principle of Structural Induction"), så får vi:
funcsMVS(func(N), [N]).
funcsMVS(mod(SL), FL) :-
funcsMVSList(SL, FL).
funcsMVSList([], []).
funcsMVSList([S|SL], FL) :-
funcsMVS(S, FL1),
funcsMVSList(SL, FL2), append(FL1, FL2, FL).
Hierarkiska mjukvarusystem: funcsMVS(S, FL)
Material 2020 för
Logikprogrammering
del av DD1351 Logik för dataloger
Thomas Sjöland sjoland@kth.se
SCS, Software and Computer Systems division CS – Computer Science department
EECS - School of Electrical Engineering and Computer Science KTH, The Royal Institute of Technology
Begränsa sökningen med snitt (cut) Negation i Prolog
Generera-testa i Prolog Programmeringstekniker
Grafer
Metaprogrammering Prolog i Prolog
DD1351 –
Logikprogrammering
föreläsning 4(4) 22 sep 2020
Prolog-specifika konstruktioner
ØSnitt
ØNegation
ØKontrollpredikat, metapredikat
Generera och testa Läsmaterial
ØBoken: kap. 5, 9, 10 ØProlog-l: misc.pl
ØHandouts: Föreläsningsanteckningar
Idag
Innehåll
Snitt (Cut)
exekvering av ett program med cut Att sätta in cut in ditt program:
”gröna" och ”röda" cut
implementation av if-then-else
Negation
grundbegrepp
exekvering av program med negation
implementation av negation i Prolog
Att kontrollera sökningen
Ibland när en lösning till ett delproblem har hittats, behöver andra lösningar till målet inte testas.
Genom att använda den icke-logiska operationen !, som läses “cut”, tar du bort alternativa grenar till delmål och till klausulen som just nu prövas.
Alternativen på en högre nivå, dvs till klausulen där det nu aktuella målet ingår behålles. Detta kan
minska mängden onödiga utsökningar.
Snitt (cut)
syntax
!, kan placeras i kroppen I en klausul som ett “mål”
p(X) :- q(X), !, r(X).
effekt
när "!" nås förhindras all backtracking till vänster om “!” i kroppen.
Exekveringen till höger fortsätter som förväntat och kan generera alternativ för senare backtracking.
nya matchningar av huvudet i senare alternativ till klausulen hindras också
Dvs. backtracking hindras en nivå upp i SLD-trädet
Snitt (cut) gör två operationer
P :- Q, !, R.
P :- ...
Tar bort alternativ till Q som ännu inte har prövats när ! passeras
Tar bort alternativ till P som inte har prövats
när ! passeras
Snitt skär bort backtrackingen för predikatet i vilkets kropp den förekommer. Kan användas för att förbättra prestandan genom att undvika onödig sökning..
Exempel:
in(H, [H | _]).
in(X, [_ | T]) :- in(X, T).
Hur många svar får vi på frågan:
?- in(2, [1, 2, 3, 1, 2, 3]).
För att eliminera svaren efter det första:
inCut(H, [H | _]) :- !.
inCut(X, [_ | T]) :- inCut(X, T).
Snitt (eng: cut)
i(1).
i(2).
j(1).
j(2).
j(3).
s(X, Y) :- q(X, Y).
s(0, 0).
q(X, Y) :- i(X), !, j(Y).
q(4, 4).
Vilka blir svaren på frågan?
?- s(X, Y).
Exempel från Learn Prolog Now!
Betrakta programmet:
max(X, Y, Z) :- X>Y, Z=X.
max(X, Y, Z) :- Z=Y.
Vad blir svaret på:
?- max(4, 3, 3).
Rättad implementation:
maxCut(X, Y, Z) :- X>Y, !, Z=X.
maxCut(X, Y, Z) :- Z=Y.
När behövs snitt?
Snitt (cut)
exempel: exekverring av ett program med cut
top(X,Y):- p(X,Y).
top(X,X) :- s(X).
p(X,Y) :- true(1), q(X), true(2), r(Y).
p(X,Y) :- s(X), r(Y).
q(a). q(b). r(c). r(d). s(e).
true(X).
?- top(X,Y).
¤ i det givna programmet (sju svar)
¤ när true(1) ersätts av ! (fem svar)
¤ när true(2) ersätts av ! (tre svar) Vad händer om både true(1) och true(2)ersätts av !
Snitt (cut)
Att sätta in cut i ditt program
För att öka effektiviteten
”gröna" cut:
Ändrar inte semantiken hos ett program (klipper bort endast grenar i ett SLD-träd som misslyckas) eller ger redundanta svar
”röda" cut:
ändrar semantiken hos programmet (klipper bort grenar som ger andra lösningar I SLD-trädet)
Generellt är röda cut uppfattas som skadliga
Snitt (cut)
”Gröna cut": ett exempel
Givet två sorterade listor med heltal Xs och Ys, konstruera en sorterad lista med heltal Zs, som innehåller element från Xs och Ys.
merge([], Ys, Ys).
merge(Xs, [], Xs).
merge([X|Xs], [Y|Ys], [X|Zs]) :- X < Y, merge(Xs, [Y|Ys], Zs).
merge([X|Xs], [Y|Ys], [X, Y|Zs]) :- X = Y, merge(Xs, Ys, Zs).
merge([X|Xs], [Y|Ys], [Y|Zs]) :- X > Y, merge([X|Xs], Ys, Zs).
Snitt (cut)
”Gröna cut": ett exempel (forts.)
merge([], Ys, Ys):- !.
merge(Xs, [], Xs):- !.
merge([X|Xs], [Y|Ys], [X|Zs]) :-
X < Y, !, merge(Xs, [Y|Ys], Zs).
merge([X|Xs], [Y|Ys], [X, Y|Zs]) :- X = Y, !, merge(Xs, Ys, Zs).
merge([X|Xs], [Y|Ys], [Y|Zs]) :- X > Y, !, merge([X|Xs], Ys, Zs).
Snitt (cut)
”Röda cut": ett exempel
Finn minimum av två heltal.
Försök:
minimum(X, Y, X) :- X =< Y, !.
minimum(X, Y, Y).
?- minimum(4,5,Z). - Yes, Z = 4.
?- minimum(5,4,Z). - Yes, Z = 4.
?- minimum(4,5,5). - Yes.
Korrigering:
minimum(X, Y, Z) :- X =< Y, !, Z=X.
minimum(X, Y, Y).
Snitt (cut)
”Röda cut": ett exempel
Kontrollera medlemskapet i en lista.
member(X, [X|Xs]) :- !.
member(X, [Y|Ys]) :- member(X, Ys).
?- member(a, [b,c,a,d]). - kontrollera medlemskapet,
?- member(X, [b,c,a,d]). OK- tar ut ett element ur en lista, tar bara ut det första elementet
?- member(b, Z). - genererar listor som innehåller b genererar bara en lista
Snitt (cut)
implementation av if-then-else
P :- Condition, !, TruePart.
P :- ElsePart.
eller med särskild syntax
(Condition -> TruePart; ElsePart) Till exempel,
minimum(X, Y, Z) :- (X =< Y -> Z = X; Z = Y).
Negation: hur man använder och bevisar negativ information?
En negerad fråga 'not_p(x)’ skall lyckas om beviset för
påståendet 'p(x)’ misslyckas och det skall misslyckas om beviset för påståendet 'p(x)’ lyckas.
%not_p(++) (++ står för en grund term) not_p(X) :- p(X), !, false.
not_p(_).
Antagandet om en sluten värld
Det som är motbevisbart på finit tid är falskt. Om det inte är motbevisbart på finit tid, anses negationen sann
animal(cow).
not_animal(X) :- animal(X), !, false.
not_animal(_).
?- not_animal(X), X=house.
Ett hus är inte ett djur så frågan skall lyckas
Tyvärr misslyckas det. Varför?
Antagandet om en sluten värld
animal(cow).
not_animal(X) :- animal(X),!, false.
not_animal(_).
not_not_animal(X) :- not_animal(X),!, false.
not_not_animal(_).
?- not_not_animal(X), X=house.
Tyvärr lyckas detta. Varför?
Negation
foundation(X) :- on(Y, X), on_ground(X).
on_ground(X) :- \+ off_ground(X).
off_ground(X) :- on(X, Y).
above(X, Y) :- on(X, Y).
above(X, Y) :- on(X, Z), above(Z, Y).
on(c, b).
on(b, a).
Negation
implementation of \+
\+ Goal :- call(Goal), !, fail.
\+ Goal.
example
p(a).
?- p(X). ja, X = a.
?- \+ \+ p(X). sant, X är inte instantierad efteråt
Betrakta programmet:
snygg(kia).
ej_snygg(X) :- snygg(X), !, fail.
ej_snygg(X).
Vad blir svaren på:
?- ej_snygg(nisse).
?- ej_snygg(kia).
Vad åstadkommer predikatet ej_snygg?
Mera snitt
Prolog utgår ifrån closed world assumption:
Allt som Prolog inte lyckas bevisa betraktas som falskt!
Negation i Prolog betraktas som bevis-teoretisk negation, och inte som logisk negation.
snygg(kia).
ej_snygg(X) :- \+ snygg(X).
Vad blir svaren på dessa queries?
?- ej_snygg(nisse).
?- ej_snygg(kia).
?- ej_snygg(X).
Negation i Prolog
Vi implementerade medlemstestet för listor så här:
in(H, [H | _]).
in(X, [_ | T]) :- in(X, T).
Vad blir svaret till:
?- in(X, [1, 2]), \+ in(X, [1,3]).
?- in(X, [1, 2]), \+ in(X, Y).
Beskriv kontrollflödet
(variant på uppgift från omtentan 2016-03-15).disjoint(X, Y) :- \+ (in(Z, X), in(Z, Y)).
Negation i Prolog
Vi betraktade redan fail, ett predikat som alltid misslyckas.
Predikatet call(X) betraktar termen som X är unifierad med som ett predikat.
snygg(kia).
not(X) :- call(X), !, fail.
not(X).
Vad blir svaret på denna fråga?
?- not(snygg(nisse)).
Kontrollpredikat
Funktioner är relationer
Funktioner är deterministiska relationer.
Det finns ett unikt värde i utdatadomänen för varje inputtupel.
En funktion f: Term* -> Term kan till exempel kodas som en relationsdefinition i ett
logikprogram som f(X1,…,Xn,Y) med ett argument med ett unikt värde Y för varje inputtupel X1,…,Xn.
Relationen f/n motsvarande en funktion med n-1 argument är deterministisk. Alla
argument (utom Y som motsvarar
resultatvärdet för funktionen) är fullt
instantierade (grunda) vid anropet.
Högre ordningens funktioner
Högre ordningens funktioner är inte direkt uttryckbara eftersom funktioner inte är
objekt i den första ordningens logiska modell vi har av ett program.
Man kan dock många gånger koda
programmeringstekniker från funktionell
programmering med en rättfram och effektiv
metod.
Högre ordningens programmering:
apply
Generell form
apply(foo,X1...Xn) :- foo(X1...Xn).
Att mappa ett predikat
Predname(In,Out) till varje element i en lista
map_list([X|Xs],Predname,[Y|Ys]) :- apply(Predname,X,Y),
map_list(Xs,Predname,Ys).
map_list([],_,[]).
Högre ordningens programmering:
map
Predikat för att hitta alla lösningar
Det kan vara meningsfullt att samla alla lösningar till en fråga i en lista.
Prolog stödjer detta genom några ”högre-ordningens" predikat.
father(sven,olle). father(sven,lisa).
father(bengt,lisa). father(bengt,sven).
children(X,Kids) :-
findall(Kid, father(X,Kid),Kids).
Målet
?- children(bengt,Kids).
ger
Kids=[lisa,sven]
Predikat för att hitta alla lösningar
father(sven,olle). father(sven,lisa).
father(bengt,lisa). father(bengt,sven).
målet
?- findall(F, father(F,Kid),Fathers).
ger
Fathers=[sven,sven,bengt,bengt]
Predikat för att hitta alla lösningar
Istället för en enda lösning som samla alla fäder till något barn kan vi vara intresserade av en separat lösning för varje barn.