Presentation laddar. Vänta.

Presentation laddar. Vänta.

Logikprogrammering 23/10 Binära träd In- och uthantering David Hjelm.

Liknande presentationer


En presentation över ämnet: "Logikprogrammering 23/10 Binära träd In- och uthantering David Hjelm."— Presentationens avskrift:

1 Logikprogrammering 23/10 Binära träd In- och uthantering David Hjelm

2 Binära träd

3 Binära träd repetition Binära träd är antingen tomma eller så består de av två delträd samt en rot. Vi noterar dem så här: –Det tomma trädet: nil. –Annars: t(V,R,H) där V är vänster delträd, H är höger delträd och R är roten, som innehåller elementet för just den noden. V och H är binära träd eva / \ pär ada / \ * * * *

4 Binära träd repetition Ett ordnat binärt träd (binary dictionary) är antingen: –Det tomma trädet nil eller –Ett träd t(V,R,H) där alla noderna i V är mindre än R, alla noderna i H är större än R och delträden V och R är ordnade my / \ anna * / \ * eva / \ * *

5 Binära träd repetition Ett balanserat binärt träd är antingen: –Det tomma trädet nil eller –Ett träd t(V,R,H) där delträden V och H är balanserade samt om V och H har nästan lika många element. Med ’nästan lika många’ menas här att det kan få finnas ett mer element i det ena delträdet än det andra men inte fler eva / \ anna my / \ / \ * * * åsa / \ * *

6 Binära träd repetition | ?- Tree = t(T1,linda,T2),T1=t(T3,eva,T4),T3=t(T5,anna,T6), T5=nil,T6=nil,T4=nil,T2=t(T7,nadja,T8),T7=t(T9,my,T10),T9=nil, T10=nil,T8=nil. T1 = t(t(nil,anna,nil),eva,nil), T2 = t(t(nil,my,nil),nadja,nil), T3 = t(nil,anna,nil), T4 = nil, T5 = nil, T6 = nil, T7 = t(nil,my,nil), T8 = nil, T9 = nil, T10 = nil, Tree = t(t(t(nil,anna,nil),eva,nil),linda,t(t(nil,my,nil), nadja,nil)) ? linda / \ evanadja / \ / \ anna * my * / \ / \ * * * *

7 Binära träd - size/2 Vi vill beräkna antalet element i ett binärt träd. För att göra detta definierar vi predikatet size/2.där första argumentet är ett binärt träd och andra argumentet är storleken. Eftersom binära träd är rekursiva datastrukturer måste vi skriva ett rekursivt predikat. Basfall: storleken på ett tomt träd är 0. Rekursionsfall: storleken på ett sammansatt träd är summan av storleken på de båda delträden + 1.

8 Binära träd - size/2 %basfall size(nil,0). %rekursionfall size(t(V,_,H),Size):- size(V,VSize), size(H,HSize), Size is Vsize + Hsize + 1.

9 ?- size(t(t(nil,anna,nil),eva,t(nil,linda,nil)),Size). 1 1 Call: size(t(t(nil,anna,nil),eva,t(nil,linda,nil)),_337)? 2 2 Call: size(t(nil,anna,nil),_886) ? 3 3 Call: size(nil,_1297) ? 3 3 Exit: size(nil,0) ? 4 3 Call: size(nil,_1291) ? 4 3 Exit: size(nil,0) ? 5 3 Call: _886 is ? 5 3 Exit: 1 is ? 2 2 Exit: size(t(nil,anna,nil),1) ? 6 2 Call: size(t(nil,linda,nil),_880) ? 7 3 Call: size(nil,_4384) ? 7 3 Exit: size(nil,0) ? 8 3 Call: size(nil,_4378) ? 8 3 Exit: size(nil,0) ? 9 3 Call: _880 is ? 9 3 Exit: 1 is ? 6 2 Exit: size(t(nil,linda,nil),1) ? 10 2 Call: _337 is ? 10 2 Exit: 3 is ? 1 1 Exit: size(t(t(nil,anna,nil),eva,t(nil,linda,nil)),3) ? Size = 3 ? yes size/2 trace

10 Binära träd - depth/2 Det kan även vara intressant att veta ett träds djup. Djupet på ett träd är den längsta möjliga vägen från roten till ett löv. Som löv räknar vi det tomma trädet. Även här definierar vi ett rekursivt predikat. Basfall: djupet på ett tomt träd är 0. Rekursionsfall: djupet på ett sammansatt träd är 1 + djupet på det djupaste av delträden.

11 Binära träd - depth/2 % djupet på ett tomt träd är 0. depth(nil,0). % djupet på ett sammansatt träd är 1 + djupet på det % djupaste av delträden. depth(t(V,_,H),Depth):- depth(V,VDepth), depth(H,HDepth), Depth is 1 + max(VDepth,HDepth). max -funktorn ingår här i det aritmetiska uttrycket som beräknas av is/2. T.ex. så gäller 3 is max(3,2).

12 ?- depth(t(nil,anna,t(t(nil,eva,nil),lisa,nil)),D). 1 1 Call: depth(t(nil,anna,t(t(nil,eva,nil),lisa,nil)),_331) ? 2 2 Call: depth(nil,_880) ? 2 2 Exit: depth(nil,0) ? 3 2 Call: depth(t(t(nil,eva,nil),lisa,nil),_874) ? 4 3 Call: depth(t(nil,eva,nil),_2035) ? 5 4 Call: depth(nil,_2446) ? 5 4 Exit: depth(nil,0) ? 6 4 Call: depth(nil,_2440) ? 6 4 Exit: depth(nil,0) ? 7 4 Call: _2035 is 1+max(0,0) ? 7 4 Exit: 1 is 1+max(0,0) ? 4 3 Exit: depth(t(nil,eva,nil),1) ? 8 3 Call: depth(nil,_2029) ? 8 3 Exit: depth(nil,0) ? 9 3 Call: _874 is 1+max(1,0) ? 9 3 Exit: 2 is 1+max(1,0) ? 3 2 Exit: depth(t(t(nil,eva,nil),lisa,nil),2) ? 10 2 Call: _331 is 1+max(0,2) ? 10 2 Exit: 3 is 1+max(0,2) ? 1 1 Exit: depth(t(nil,anna,t(t(nil,eva,nil),lisa,nil)),3) ? D = 3 ? yes depth/2 trace

13 Lägga till element i ett ordnat binärt träd Vi har ett ordnat binärt träd och vill lägga till ett nytt element Nytt. Detta är ett enkelt sätt att göra det på: Om trädet är tomt, nil, så skapar vi helt enkelt ett nytt träd t(nil,Nytt,nil). Om trädet är sammansatt, t(V,R,H), finns tre alternativ: –Nytt är mindre än R. I så fall lägger vi till Nytt i V. –Nytt är större än R. I så fall lägger vi till Nytt i H. –Nytt och R är lika. I så fall behöver vi inte lägga till Nytt. För att täcka dessa fall behöver vi alltså fyra klausuler. Vi definierar predikatet add/3 som tar ett binärt träd och ett element och returnerar ett nytt träd som resultat.

14 Binära träd - add/3 %add(Nytt,Träd,NyttTräd) add(Nytt, nil, t(nil,Nytt,nil)). add(Nytt, t(V,Nytt,H), t(V,Nytt,H)). add(Nytt, t(V,Rot,H), t(NyttV,Rot,H)):- Rot, add(Nytt, V, NyttV). add(Nytt, t(V,Rot,H), t(V,Rot,NyttH)):- Y, add(Nytt, H, NyttH).

15 ?- add(lena,t(nil,eva,t(nil,my,nil)),T). 1 Call: add(lena,t(nil,eva,t(nil,my,nil)),_302) ? 2 Call: ? 2 Fail: ? 2 Call: ? 2 Exit: ? 2 Call: add(lena,t(nil,my,nil),_827) ? 3 Call: ? 3 Exit: ? 3 Call: add(lena,nil,_1990) ? 3 Exit: add(lena,nil,t(nil,lena,nil)) ? 2 Exit: add(lena,t(nil,my,nil),t(t(nil,lena,nil),my,nil)) ? 1 Exit: add(lena,t(nil,eva,t(nil,my,nil)), t(nil,eva,t(t(nil,lena,nil),my,nil))) ? T = t(nil,eva,t(t(nil,lena,nil),my,nil)) ? yes add/3 - trace

16 Lägga till element i balanserade binära träd Ett problem med det add-predikat vi definierat är att balanserade träd kan bli obalanserade om vi stoppar in nya element. Om man till exempel stoppar in elementen i ordning händer detta: ?- add(anna,nil,T1),add(eva,T1,T2),add(lisa,T2,T3). T1 = t(nil,anna,nil), T2 = t(nil,anna,t(nil,eva,nil)), T3 = t(nil,anna,t(nil,eva,t(nil,lisa,nil)))

17 Lägga till element i balanserade binära träd Det finns sätt att återbalansera träd efter att man har stoppat in element och det finns specialträd som alltid är balanserade (se kap 10 i boken). Ingår inte i denna kurs. Bra informell metod är att stoppa in elementen i oordning.

18 Ta bort element från ordnade binära träd Det är svårare att ta bort element än att lägga till dem. Antag att det är roten i trädet som ska bort. I så fall finns det två delträd nedanför elementet, som måste kombineras till ett nytt träd. Antag att elementet som ska bort är R, roten i trädet t(V,R,H ). Då är resultatet ett träd med elementen i V och H, och dessutom ska det vara ordnat. Leta upp ett passande element E i något av delträden, antingen det största elementet i V eller det minsta elementet i H, ta bort E från delträdet och stoppa in E istället för R. Står närmare beskrivet på sid. 209ff i kursboken.

19 Ta bort element från balanserade träd Problemet med att ta bort element är detsamma som med att lägga till element - trädet kan bli obalanserat. Det finns naturligtvis mer sofistikerade sätt att ta bort element från träd. Ingår inte heller i kursen.

20 In- och uthantering

21 Det finns i Prolog predikat för att skriva till skärmen och till filer samt läsa från skärmen och från filer. Förutom att läsa och skriva tecken kan man läsa och skriva termer. Det är ju precis det som interpretatorn gör när man ställer en fråga till systemet. SICStusmanualen tar upp en mängd olika predikat för in- och uthantering. Vissa av dessa gäller bara för SICStus Prolog. Idag ska vi främst ta upp write/1, read/1, put_char/1 och get_char/1. De tillhör alla ISO- standarden.

22 Uthantering - write/1 Predikatet write/1 skriver ut en term på skärmen: ?- write(ellen). ellen yes Argumentet till write/1 kan vara vilken term som helst. Variabler blir dock inte så snyggt eftersom de direkt döps om internt av prolog: ?- write(term(a,[b,c],D)). term(a,[b,c],_239) true ? yes

23 Uthantering - write/1,nl/0 Predikatet nl/0 skriver ut en radmatning. Det går också att använda write('\n'). ?- write(a),nl,write(b),write(’\n’),write(c). a b c yes

24 Uthantering - write/1,nl/0 För att skriva ut mellanslag och stora bokstäver måste man använda ’’ ?- write('A B C'). A B C yes ?- write('et'),nl,write(' tu'),nl,write(' Brute'). et tu Brute yes

25 Inhantering - read/1 Predikatet read/1 läser in en prologterm från tangentbordet. Argumentet till read/1 är en variabel som instantieras till det man matar in. Det man skriver in måste vara en korrekt prologterm, och måste avslutas med punkt. Annars blir det syntaxfel. Skriver man in något med stor bokstav (utan citationstecken) tolkas det som en variabel. | ?- read(X). |: sover(lisa). X = sover(lisa) ? yes

26 Inhantering - read/1 Man kan även läsa in tal och strängar. | ?- read(X),read(Y). |: 1. |: "ett". X = 1, Y = [e,t,t] ? yes

27 Inhantering - read/1 Man kan även använda operatorer: ?- read(X). |: 1*2+3. X = 1*2+3 ? yes Skriver man en variabel så tolkas den som en ny okänd variabel: | ?- read(X). |: ETT:2:TRE. X = _A:2:_B ? yes

28 Uthantering - put_char/1 Predikatet put_char/1 skriver ut ett tecken i taget: ?- put_char(e),put_char(v),put_char(a),put_char('\n'). eva yes put_char/1 kan inte skriva ut något annat än tecken: | ?- put_char(A). {INSTANTIATION ERROR: put_char(_144) - arg 1} | ?- put_char(2). {TYPE ERROR: put_char(2) - arg 1: expected character, found 2}

29 Inhantering - get_char/1 put_char/1 är inte så användbart, man kan ju alltid använda write/1 istället. Dess motsvarighet get_char/1 är däremot väldigt användbar. Med get_char/1 kan man läsa in (nästan) vad som helst tecken för tecken. ?- get_char(C). |: a C = a ? Här kan vi dessutom använda stora bokstäver och siffror, men de kommer alltid bli tecken i Prolog: ?- get_char(C1),get_char(C2). |: A2 C1 = 'A', C2 = '2' ? yes

30 Backtracking i in- och uthantering Backtracking fungerar inte för in-/uthanteringspredikaten. Anledningen till det är att Prolog interagerar med operativsystemet i övrigt som inte har en aning om vad backtracking innebär. read/1, write/1, get_char/1 och put_char/1 utförs alltid, oberoende huruvida frågan lyckas eller ej: ?- Term = lisa, write(Term), Term = pelle. lisa no Om vi hade bytt plats på write(Term) och Term = pelle i frågan ovan så hade inget skrivits ut.

31 Inhantering - get_line/1 Vi vill läsa in en hel rad till en sträng. För att göra detta definierar vi predikatet get_char/1 som anropar get_char/1 tills ’\n’ har lästs. get_line(Str) :- get_char(X), ( X = ’\n' -> Str = [] ; Str = [X|Rest], get_line(Rest) ). OBS!!! OBS!!!: Vi får bara anropa get_char/1 en gång per tecken. Därför kan vi inte dela upp get_line/1 på två klausuler - ett basfall och ett rekursionsfall.

32 Inhantering - get_line/1 ?- get_line(Str). |: En rad med tecken. Str = ['E',n,' ',r,a,d,' ',m,e,d,' ',t,e,c,k,e,n,'.'] ? yes ?- get_line(Str1),get_line(Str2). |: en rad |: en annan rad Str1 = [e,n,' ',r,a,d], Str2 = [e,n,' ',a,n,n,a,n,' ',r,a,d] ? yes

33 Det var allt!


Ladda ner ppt "Logikprogrammering 23/10 Binära träd In- och uthantering David Hjelm."

Liknande presentationer


Google-annonser