1 / 33

ackumulatorer

ackumulatorer. CM 3.7 AOP 8.3 (BBS 5.3, 6.2.2). dagens föreläsning. rekursion iteration ackumulatorer för effektivitet exempel. 1. summera (repetition). summerar alla tal mellan N och M: summera(N, N, N) . summera(N, M, Summa) :- N < M , N1 is N+1 , summera(N1, M, Delsumma) ,

daktari
Download Presentation

ackumulatorer

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. ackumulatorer CM 3.7 AOP 8.3 (BBS 5.3, 6.2.2)

  2. dagens föreläsning • rekursion • iteration • ackumulatorer för effektivitet • exempel

  3. 1. summera (repetition) • summerar alla tal mellan N och M: summera(N, N, N) . summera(N, M, Summa) :- N < M , N1 is N+1 , summera(N1, M, Delsumma) , Summa is N + Delsumma .

  4. körning av summera ?- summera(1,5,X). 1 1 Call: summera(1,5,_454) ? 2 2 Call: 1<5 ? 2 2 Exit: 1<5 ? 3 2 Call: _981 is 1+1 ? ... 4 2 Call: summera(2,5,_973) ? 7 3 Call: summera(3,5,_3556) ? 10 4 Call: summera(4,5,_6139) ? 13 5 Call: summera(5,5,_8722) ? … 17 2 Exit: 15 is 1+14 ? 1 1 Exit: summera(1,5,15) ? X = 15 ?

  5. effektivitet… antag följande körning: ?- summera(1, 1000, X1). prolog måste då lösa: summera(2, 1000, X2), X1 is 1+X2 summera(3, 1000, X3), X2 is 2+X3, X1 is 1+X2 o.s.v. tills: summera(999, 1000, X999), X998 is 998+X999, …, X2 is 2+X3, X1 is 1+X2

  6. effektivitet… • detta ger en ganska lång ”att-göra-lista”! • när det tusende rekursiva anropet har nåtts, måste prolog ”komma ihåg” att göra 1000 additioner… • istället kan vi använda ackumulatorer genom att: • lägga till ett extra argument som representerar den delsumma vi fått hittills.

  7. naturligt? • vi tar det första talet (t ex 1) • och lägger till det andra talet (2+1) • sedan lägger vi till 3 till summan av föregående steg (3+3) • osv: 4+6, 5+10, 6+15, …

  8. summera med ackumulator accSummera(N, N, Acc, Summa) :- Summa is N + Acc . accSummera(N, M, Acc, Summa) :- N < M , N1 is N+1 , Acc1 is N + Acc , accSummera(N1, M, Acc1, Summa) .

  9. körning av summera (igen) ?- summera(1,5,X). 1 1 Call: summera(1,5,_454) ? 2 2 Call: 1<5 ? 2 2 Exit: 1<5 ? 3 2 Call: _981 is 1+1 ? ... 4 2 Call: summera(2,5,_973) ? 7 3 Call: summera(3,5,_3556) ? 10 4 Call: summera(4,5,_6139) ? 13 5 Call: summera(5,5,_8722) ? … 17 2 Exit: 15 is 1+14 ? 1 1 Exit: summera(1,5,15) ? X = 15 ?

  10. fix • summera2 kallar accSummera med ackumulatorn satt till 0 • mer om detta snart!

  11. körning av accSummera ?- summera2(1,5,X). 1 1 Call: summera2(1,5,_454) ? 2 2 Call: accSummera(1,5,0, _454) ? 3 3 Call: 1<5 ? 3 3 Exit: 1<5 ? … 6 3 Call: accSummera(2,5,1, _454) ? … 10 4 Call: accSummera(3,5,3, _454) ? 14 5 Call: accSummera(4,5,6, _454) ? 18 6 Call: accSummera(5,5,10, _454) ? 1 1 Exit: summera2(1,5,15) ? X = 15 ?

  12. slutsatser • lika många steg • men: minnesutrymmet konstant i ackumulatorvarianten (oberoende av antalet iterationer)

  13. imperativa språk… int räknare = 0; while (räknare < 10) { räknare = räknare + 1; } return räknare; • prolog har inga ’storage variables’ (som t.ex. ’räknare’ ovan) • därför måste vi använda rekursion i prolog

  14. 2. length (repetition) • längden av en lista kan ges av predikatet length/2: length([],0) . length([_|Tail], N) :- length(Tail, N1), N is 1 + N1 .

  15. length med ackumulator accLength([_|Tail],Acc,L) :- Acc1 is A+1, accLength(Tail,Acc1,L) . accLength([],A,A) .

  16. sammanfattning • prolog tillhandahåller inte iteration • vi itererar med hjälp av den mer generella rekursionen • fördelen med ”sann” iteration är effektivitet eftersom: • för varje rekursivt anrop som inte terminerats måste en struktur (’stack frame’) hållas i minnet

  17. sammanfattning (forts.) • alltså: ett rekursivt program växer linjärt. • en iterativ procedur använder en konstant mängd utrymme (oberoende av antal iterationer) • ett sätt att göra rekursiva procedurer iterativa är att använda ackumulatorer

  18. ytterligare effektivitet • kan vi med hjälp av ackumulatorer ytterligare effektivisera programmeringen? • japp, i vissa fall:

  19. 3. reverse • [a,b,c,d] ska reverseras till [d,c,b,a] • två versioner: • naiveRev/2 • accRev/3 (rev/2)

  20. naiveRev • basfall: • om vi reverserar [], så får vi tomma listan ([]) • rekursiva fallet: • om vi reverserar [H|Tail], så slutar vi med listan vi får av att reversera Tail och sätta ihop den med H.

  21. naiveRev/2 • alltså: naiveRev([],[]). naiveRev([H|Tail],R) :- naiveRev(Tail,RevT), append(RevT,[H],R).

  22. trace: naiveRev ?- naiveRev([a,b,c],R). 1 1 Call: naiveRev([a,b,c],_227) ? 2 2 Call: naiveRev([b,c],_699) ? 3 3 Call: naiveRev([c],_1069) ? 4 4 Call: naiveRev([],_1438) ? 4 4 Exit: naiveRev([],[]) ? 5 4 Call: append([],[c],_1069) ? 5 4 Exit: append([],[c],[c]) ? 3 3 Exit: naiveRev([c],[c]) ? 6 3 Call: append([c],[b],_699) ? 7 4 Call: append([],[b],_3515) ? 7 4 Exit: append([],[b],[b]) ? 6 3 Exit: append([c],[b],[c,b]) ? 2 2 Exit: naiveRev([b,c],[c,b]) ? 8 2 Call: append([c,b],[a],_227) ? 9 3 Call: append([b],[a],_5245) ? 10 4 Call: append([],[a],_5609) ? 10 4 Exit: append([],[a],[a]) ? 9 3 Exit: append([b],[a],[b,a]) ? 8 2 Exit: append([c,b],[a],[c,b,a]) ? 1 1 Exit: naiveRev([a,b,c],[c,b,a]) ? R = [c,b,a]

  23. resultat 20 steg i tracen

  24. accRev/3 accRev([H|Tail],A,R) :- accRev(Tail,[H|A],R). accRev([],A,A).

  25. trace: accRev ?- accRev([a,b,c],[],R). 1 1 Call: accRev([a,b,c],[],_491) ? 2 2 Call: accRev([b,c],[a],_491) ? 3 3 Call: accRev([c],[b,a],_491) ? 4 4 Call: accRev([],[c,b,a],_491) ? 4 4 Exit: accRev([],[c,b,a],[c,b,a]) ? 3 3 Exit: accRev([c],[b,a],[c,b,a]) ? 2 2 Exit: accRev([b,c],[a],[c,b,a]) ? 1 1 Exit: accRev([a,b,c],[],[c,b,a]) ? R = [c,b,a]

  26. resultat 8 steg i tracen

  27. accRev lista ackumulator [a,b,c] [] [b,c] [a], dvs: [a|[]] [c] [b,a] [] [c,b,a] • ackumulatorn kopieras sedan till accRev/3 tredje argument • accRev([],A,A). • vi effektiviserar alltså bort ’append’

  28. ett vanligt ”fix” % dölj ackumulatorn vid användning rev(L,R) :- accRev(L,[],R). • rev/2 anropas alltså med samma antal argument som vid naiveRev/2

  29. jämförelse • naiveRev (3 element): 20 instruktioner • accRev (3 element): 8 instruktioner • naiveRev (8 element): 90 instruktioner • accRev (8 element): 18 instruktioner • och som sagt: minnesutrymmet växer linjärt i naiveRev, • men är konstant i accRev

  30. har vi sett det här förut? • accRev igen: accRev([H|Tail],A,R) :- accRev(Tail,[H|A],R). • skriv om som: accRev([H|Tail]) --> accRev(Tail),[H].

  31. körning… ?- accRev([a,b,c],X,[]). 1 1 Call: accRev([a,b,c],_476,[]) ? 2 2 Call: accRev([b,c],_476,_1034) ? 3 3 Call: accRev([c],_476,_1538) ? 4 4 Call: accRev([],_476,_2042) ? 4 4 Exit: accRev([],_476,_476) ? 5 4 Call: 'C'(_476,c,_1538) ? 5 4 Exit: 'C'([c|_1538],c,_1538) ? 3 3 Exit: accRev([c],[c|_1538],_1538) ? 6 3 Call: 'C'(_1538,b,_1034) ? 6 3 Exit: 'C'([b|_1034],b,_1034) ? 2 2 Exit: accRev([b,c],[c,b|_1034],_1034) ? 7 2 Call: 'C'(_1034,a,[]) ? 7 2 Exit: 'C'([a],a,[]) ? 1 1 Exit: accRev([a,b,c],[c,b,a],[]) ? X = [c,b,a]

  32. sammanfattning • ackumulatorer är ett generellt sätt att med hjälp av rekursion implementera iteration • iteration är utrymmeseffektivt (konstant under iterationerna) medan rekursion växer linjärt med antalet iterationer • ibland är också iteration processeffektivt (t ex vid reversering av listor)

  33. frågor etc: ponjo@ida.liu.se

More Related