Logikprogrammering
Ons, 25/9
Rebecca Jonson
Repetition
• Jämförelser av tal <, >, =<, >=
• Ordnade listor
?- stigande_ordning([2,4,6,8,9]).
Yes
• Sortering
• Bubble Sort
• Insertion sort (insättningssortering)
• Quicksort
Repetition
• Insättningsortering
– En sammansatt lista sorteras genom att
sortera svansen på listan och sedan sätta in
huvudet i den ordnade listan på lämplig plats
så att resultatlistan blir sorterad. Den tomma
listan är redan sorterad.
Repetition
Insättning i ordnad lista
insert(Tal,[],[Tal]).
insert(Tal,[N|Ns],[Tal,N|Ns]):Tal<N.
insert(Tal,[N|Ns],[N|NMs]:M>N,
insert(Tal,Ns,NMs).
Insättningssortering
insertsort([],[]).
insertsort([Huvud|Svans],SorteradLista):insertsort(Svans,SorteradSvans),
insert(Huvud,SorteradSvans,SorteradLista).
Repetition
• Quicksort
– Tar en lista och ett element X i listan. Delar
listan i två: den ena med elementen som är
mindre än X och den andra med elementen
som är större än X. Sortera sedan dessa två
listor med samma metod för att slutligen sätta
ihop de två listorna till en med elementet X
som länk eftersom X är större än alla element
i lilla listan och mindre än elementen i ”stora”.
Repetition
quicksort([],[]).
quicksort([X|Xs],Sorted):delning(Xs,X,Littles,Bigs),
quicksort(Littles,Ls),
quicksort(Bigs,Bs),
append(Ls,[X|Bs],Sorted).
Delning
delning([],Y,[],[]).
delning([X|Xs],Y,[X|Ls],Bs):X =< Y,
delning(Xs,Y,Ls,Bs).
delning([X,|Xs],Y,Ls,[X|Bs]):X > Y,
delning(Xs,Y,Ls,Bs).
Dagens föreläsning
•
•
•
•
•
•
•
True och fail
Konstruktionen if-then-else
Filtrering med if-then-else
Mappning med if-then-else
Sortering med if-then-else
Negation som if-then-else
Fördelar/Nackdelar med if-then-else
Predikaten true/0 och fail/0
• Predikatet true/0 är ett predikat som alltid
lyckas, och predikatet fail/0 misslyckas
alltid.
?- true. Yes
?- fail. no
Konstruktionen if-then-else
• En mer generell konstruktion än negation
är if-then-else som i Prolog skrivs som
frågan test->if_true; if_fail?. Detta betyder
ungefär ''om frågan test lyckas, fortsätt
med if_true, annars med if_fail''.
Observera att semikolonet i detta fall har
en liten annan betydelse än i vanliga fall.
(PQ ; R) = if P then Q else R
Filtrering med if-then-else
• If-then-else kan användas överallt där vi har använt
negation hittills - när vi vill välja mellan två eller flera
olika alternativ. Till exempel, om vi vill filtrera ut de ord
som börjar med stor bokstav ur en lista kan vi använda
oss av negation som förut.
stora_ord([], []).
stora_ord([StortOrd|Resten],[StortOrd|StoraOrd]) :börjar_på_stor_bokstav(StortOrd),
stora_ord(Resten, StoraOrd).
stora_ord([LitetOrd|Resten], StoraOrd) :\+ börjar_på_stor_bokstav(LitetOrd),
stora_ord(Resten, StoraOrd).
Två rekursionsfall blir ett
• Men vi kan också använda oss av konstruktionen if-thenelse. Då måste vi först slå ihop de två rekursionsfallen till
ett enda, med en disjunktion.
stora_ord([], []).
stora_ord([Ord|Resten], StoraOrd) :( börjar_på_stor_bokstav(Ord),
StoraOrd = [Ord|ResterandeStoraOrd],
stora_ord(Resten, ResterandeStoraOrd)
;
\+ börjar_på_stor_bokstav(Ord),
stora_ord(Resten, StoraOrd) ).
Med If-then-else
• Sedan kan vi förvandla test,if_true;\+test,if_fail? till
test->if_true;if_fail?.
stora_ord([], []).
stora_ord([Ord|Resten], StoraOrd) :( börjar_på_stor_bokstav(Ord) ->
StoraOrd = [Ord|ResterandeStoraOrd],
stora_ord(Resten, ResterandeStoraOrd)
;
stora_ord(Resten, StoraOrd)
).
Stora_ord
• Med lämplig definition av predikatet
börjar_på_stor_bokstav/1 kan predikatet
fungera så här.
?-stora_ord([”Eva","älskar",”Adam"],Stora).
Stora = [”Eva",”Adam"]
Mappning med if-then-else
• Ett annat exempel är om vi vill mappa ett
predikat på endast vissa element i en lista.
Säg att vi vill multiplicera varje positivt tal
med sig självt, och de negativa (och noll)
vill vi behålla. Ett passande hjälppredikat
är då kvadrera_positivt/2
kvadrera_positivt(Tal, Kvadrat) :Tal > 0,
Kvadrat is Tal*Tal.
Med negation
Följande är då en definition av kvadrera_positiva/2
som använder sig av negation.
kvadrera_positiva([], []).
kvadrera_positiva([Tal|Resten],[Kvadrat|Kvadrater]
) :kvadrera_positivt(Tal, Kvadrat),
kvadrera_positiva(Resten, Kvadrater).
kvadrera_positiva([Tal|Resten], [Tal|Kvadrater]) :\+ kvadrera_positivt(Tal, Kvadrat),
kvadrera_positiva(Resten, Kvadrater).
Med disjunktion
• För att kunna använda oss av if-then-else måste vi först
slå ihop de två rekursionsfallen till en klausul med en
disjunktion.
kvadrera_positiva([], []).
kvadrera_positiva([Tal|Resten],[NyttTal|Kvadrater]) :( kvadrera_positivt(Tal, NyttTal),
kvadrera_positiva(Resten, Kvadrater)
;
\+ kvadrera_positivt(Tal, _),
NyttTal = Tal,
kvadrera_positiva(Resten, Kvadrater) ).
Med if then else
• Sedan kan vi förvandla test,if_true;\+test,if_fail?
till test->if_true;if_fail?.
kvadrera_positiva([], []).
kvadrera_positiva([Tal|Resten], [NyttTal|Kvadrater])
:( kvadrera_positivt(Tal, NyttTal) ->
kvadrera_positiva(Resten, Kvadrater)
;
NyttTal = Tal,
kvadrera_positiva(Resten, Kvadrater)
).
Med True
Observera att vi kan, om vi vill, flytta ut frågan
kvadrera_postitiva(Resten,Kvadrater)? ur if-then-else,
eftersom den förekommer både på true- och fail-sidan.
Och då måste vi stoppa in en fråga som alltid lyckas på
true-sidan eftersom den annars blir helt tom.
kvadrera_positiva([], []).
kvadrera_positiva([Tal|Resten], [NyttTal|Kvadrater]) :( kvadrera_positivt(Tal, NyttTal) ->
true
;
NyttTal = Tal ),
kvadrera_positiva(Resten, Kvadrater).
Sortering med if then else
• Måndagens predikat insert/3 kan även skrivas
med if-then-else. Så här såg det ut i måndags.
insert(M, [], [M]).
insert(M, [N|Ns], [M,N|Ns]) :M < N.
insert(M, [N|Ns], [N|NMs]) :M > N,
insert(M, Ns, NMs).
Ett rekursionsfall
De två rekursionsfallen kan slås ihop till ett enda.
insert(M, [], [M]).
insert(M, [N|Ns], NMs) :( M < N,
NMs = [M,N|Ns]
;
M > N,
NMs = [N|Ms],
insert(M, Ns, Ms)
).
Insert med if-then-else
• Och vi kan införa en if-then-else över jämförelsen.
insert(M, [], [M]).
insert(M, [N|Ns], NMs) :( M < N ->
NMs = [M,N|Ns]
;
NMs = [N|Ms],
insert(M, Ns, Ms)
).
Insert
• Observera dock att det blir en liten
skillnad, eftersom den förra versionen inte
lyckas om man vill stoppa in ett element
som redan finns medan den nya versionen
lyckas och lägger in en dubblett.
?- insert(3, [1,2,3,4], Xs). Xs = [1,2,3,3,4]
Flera if-then-else
insert(M, [], [M]).
insert(M, [N|Ns], NMs) :( M < N ->
NMs = [M,N|Ns]
;
M = N ->
NMs = [N|Ns]
;
NMs = [N|Ms],
insert(M, Ns, Ms)
).
• Antag att vi i definitionen
av insert/3 även vill ha
ett specifikt alternativ för
när elementet redan finns
i listan. Då kan vi skriva
en ny if-then-else i elsedelen.
• Detta säger då att om
elementet redan finns i
listan så gör vi ingen
förändring alls.
?- insert(3, [1,2,3,4], Xs).
Xs = [1,2,3,4]
Negation som if-then-else
• Varje negation kan skrivas utan \+ och
med -> ; istället. Vi översätter helt enkelt
\+fråga? till fråga->fail;true?.
• Observera att detta inte betyder att med
bör göra så, det är helt enkelt enklare att
läsa en negation än en if-then-else.
Fördelar med if-then-else
• Fördelarna med att använda if-then-else
istället för negation är att man kan få
snabbare program, eftersom testet bara
behöver utföras en enda gång. Frågan p>q;r? kommer att anropa p endast en
gång, medan frågan p,q;\+p,r? kommer
ibland att anropa p två gånger (först för att
kolla om p lyckas, sedan för att kolla om p
misslyckas).
Nackdelar med if-then-else
• Nackdelarna är mer estetiska - det ser oftare lite
mer oläsligt ut med if-then-else än att använda
negation. Detta för att man måste slå ihop flera
klausuler som kanske är läsligare var för sig.
• För att göra det läsligare skriv på separata rader.
Alltså, frågan p->q,r;s,t? bör skrivas
( p ->
q,
r
;
s,
t
)
Lyckas endast en gång
• En viktig sak med if-then-else är att testet (if-delen)
lyckas endast en enda gång, om det överhuvud taget
lyckas. Detta ska egentligen inte ha någon större
betydelse, eftersom man endast ska använda testet till
just tester, som endast kan lyckas eller misslyckas. Det
är inte meningen att man ska använda frågor som kan
ge flera olika svar.
?- ( member(X,"abc") -> member(X,"bcd") ; blaha ).
no
Ovanstående misslyckas eftersom testet
member(X,"abc")? lyckas med X=a, och det blir det enda
svaret. Prolog kan helt enkelt inte göra backtracking
tillbaka till testet när true-delen member(a,"bcd")
misslyckas. Tyvärr.
Tack för mig!
Och lycka till med Prolog!