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. (PQ ; 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!