Modelleren en Programmeren Deeltoets 2. Proefopgaven Het tentamen bestaat uit tien vragen, elk goed voor drie punten. minimaal 16.5 uit 30 punten haalt. Je bent geslaagd als je Opgave 1 Neem de volgende queries over, en geef aan hoe Prolog erop reageert.?- X = mia, X = Vincent.?- X = mia, X = vincent.?- k(s(g),y) = k(x,t(k)).?- k(s(g),t(k)) = k(x,t(y)).?- [X []] = [a].?- [X,[]] = [a,[] []]. Opgave 2 Hier de definitie van optellen voor natuurlijke getallen in successornotatie. add(0,x,x). add(s(x),y,s(z)) :- add(x,y,z).... 1
Opgave 3 Hieronder de definitie van een predicaat times/3 waarmee je vermenigvuldigen modelleert als herhaald optellen. times_acc(x,y,z) :- times(x,y,0,z). % wrapper times(s(x),y,a,z) :- add(y,a,a1), times(x,y,a1,z). times(0,_,a,a). Definieer nu modulo/3 als herhaald aftrekken. De relatie modulo(x,y,z) drukt uit dat Z de rest is van delen van X door Y. Je definitie heeft een grensgeval en een recursief geval. - Het grensgeval wordt bereikt als X kleiner is dan de deler Y. In dat geval is X zelf de gezochte rest. - Het recursieve geval formuleert modulo rekenen als herhaald aftrekken. Denk erom dat de deler groter moet zijn dan nul. Voorbeeld:?-modulo(s(s(s(0))),s(s(0)),Z). Antwoord: Z = s(0). 2
Opgave 4 Hieronder een programma len/2 om de lengte van een lijst uit te rekenen. len([],0). len([_ T],N) :- len(t,m),n is M+1. De definitie is links-recursief. Geef een betere definitie met behulp van een predicaat len/3 dat van een accumulator gebruik maakt: len(lijst,acc,lengte) slaagt als Lengte de lengte is van Lijst uitgerekend met behulp van Acc als accumulator. len/3 loopt van links naar rechts door de invoerlijst, en hoogt de accumulator bij elke stap op. Als het eind van de invoerlijst is bereikt, is de gezochte lengte gelijk aan de waarde van de accumulator. Geef een wrapper, die de goede startwaarde voor de accumulator invult voor de?? hieronder. Definieer dan het recursieve en het grensgeval voor len/3. len(lijst,lengte) :- len(lijst,??,lengte). Opgave 5 Het ingebouwde predicaat select/3 werkt als volgt: select(e,lijst,rest) slaagt als Rest de lijst is die overblijft als je een voorkomen van element E uit Lijst verwijdert, en faalt als E niet in Lijst voorkomt. Bijvoorbeeld:?- select(a,[b,a,c,a],l). L = [b, c, a] ; L = [b, a, c] ; false. Definieer select/3. Je definitie heeft een basisgeval, en een recursief geval. Je mag geen hulppredicaten gebruiken. 3
Opgave 6 We kunnen een getal in binaire notatie in Prolog representeren als een niet lege lijst van 0-en en 1-en. 0 10 wordt dan [0], 5 10 wordt [1,0,1], enzovoort. Definieer een predicaat bitnot/2 waarmee je een dergelijke lijst omzet in zijn bitsgewijze complement: 0 wordt 1 en omgekeerd. Bijvoorbeeld:?- bitnot([1,0,0,1],l). L = [0,1,1,0]. Opgave 7 Om een decimaal getal n 10 om te rekenen naar m 2 in binaire notatie is er het volgende eenvoudige algoritme. Deel n door twee: de rest is de laatste bit van m; voor de voorlaatste bit herhaal je met het naar beneden afgeronde resultaat van n/2; ga zo door tot het resultaat van verder delen nul wordt. Schrijf een Prolog programma dec2bin/2: dec2bin(dec,bin) zet decimaal getal Dec om in binair Bin, gerepresenteerd als lijst. Werk voor decimale invoer groter dan nul met een accumulator. Hieronder alvast het grensgeval, en de wrapper die dec2bin/3 aanroept met [] als beginwaarde voor de accumulator. Je kan de ingebouwde rekenpredicaten gebruiken: N//M voor delen afgerond op gehele getallen, en N mod M voor modulo rekenen. Hieronder een illustratie van de werking van die rekenpredicaten. dec2bin(0,[0]). dec2bin(n,bin) :- N>0, dec2bin(n,[],bin).?- X is 5//2.?- X is 5 mod 2. X = 2. X = 1. 4
Opgave 8 Een binaire boom is leeg, of hij bestaat uit een wortelknoop met een linker en een rechter dochterboom, waarbij die dochters zelf binaire bomen zijn. De lege boom representeren we in Prolog als [], een niet-lege boom als een term t(links,knoop,rechts). We noemen een binaire boom welgebalanceerd als voor elke knoop geldt dat het aantal knopen in zijn linker en rechter dochterboom op zijn hoogst met één verschillen. Hieronder de vier mogelijkheden om een welgebalanceerde boom met vier knopen te maken. Definieer een predicaat balans/2 waarmee je de welgebalanceerde binaire bomen genereert. balans(n,boom) slaagt als Boom een welgebalanceerde boom is met N knopen. Het aantal knopen druk je uit als een natuurlijk getal in successornotatie. De aanroep die de vier bomen van het plaatje hierboven genereert ziet er als volgt uit.?- balans(s(s(s(s(0)))),boom). Boom = t(t(t([], _, []), _, []), _, t([], _, [])) ; Boom = t(t([], _, t([], _, [])), _, t([], _, [])) ; Boom = t(t([], _, []), _, t(t([], _, []), _, [])) ; Boom = t(t([], _, []), _, t([], _, t([], _, []))) ; false. Het basisgeval voor balans/2 is eenvoudig: de lege boom is de enige welgebalanceerde boom met nul knopen. Voor het recursieve geval heb je een knoop nodig voor de wortel. Er blijft een even of oneven aantal knopen over die je dan over de linker en rechter dochters van de wortel 5
kan verdelen. Zoals bij de opgave over modulo rekenen is add/3 het enige hulppredicaat dat je mag gebruiken. Opgave 9 Een postorder traversal van een binaire boom bezoekt de knopen van de linker en rechter deelboom in postorder volgorde, gevolgd door de wortelknoop. Bijvoorbeeld: a b c d e?-b = t(t(_,a,_),b,t(t(_,c,_),d,t(_,e,_))), postorder(b,lijst). Lijst = [a,c,e,d,b]. Hieronder een naieve definitie van postorder/2 die gebruik maakt van append/3. postorder(t(l,x,r),knopen):- postorder(l,linkerknopen), postorder(r,rechterknopen), append(rechterknopen,[x],rechts), append(linkerknopen,rechts,knopen). postorder([],[]). Werk de append/3 aanroepen weg door gebruik te maken van verschillijsten. De wrapper: postorder(boom,knopen) :- postorder(boom,knopen,[]). 6
Opgave 10 Het ingebouwde findall/3 gebruik je om alle oplossingen voor een vraag in een lijst te verzamelen: findall(object,goal,list) slaagt als List de lijst van objecten Object is die voldoen aan Goal. Bijvoorbeeld:?- findall(prefix,append(prefix,_,[a,b,c,d]),lijst). Lijst = [[], [a], [a, b], [a, b, c], [a, b, c, d]]. Hieronder de definitie van shuffle/3 voor het in elkaar voegen van twee lijsten. shuffle([],[],[]). shuffle([h R],S,[H T]) :- shuffle(r,s,t). shuffle(r,[h S],[H T]) :- shuffle(r,s,t). Laten we een verzameling representeren als een lijst zonder herhalingen. Gebruik findall/3 en shuffle/3 om een definitie te geven van powerset/2: powerset(set,subsets) slaagt als Subsets de lijst van deelverzamelingen is van de verzameling Set. Bijvoorbeeld:?- powerset([1,2,3],l). L = [[1, 2, 3], [1, 2], [1, 3], [1], [2, 3], [2], [3], []]. 7