Deklarativt programmeringsparadigm Det vi introducerade på förra föreläsningen var ett exempel på deklarativ programmering. Vi specificerade en fallanalys som innehöll fakta och regler för hur man skulle dra slutsatser ur fakta. Med enkla relationer kunde vi specificera hur olika instanser av ett problem hängde samman och prolog lät oss få svar efter svar tills hela det universum som våra fomuleringar definierade hade gåtts igenom. Vi specificerade resultatets egenskaper snarare än vilka beräkningar som ska utföras. Det finns flera språk som stödjer deklarativ programmering i olika former. Varför då introducera först funktionellt, imperativt och objektorienterat paradigm genom att successivt introducera begreppsapparaten för datorspråk och sedan deklarativt paradigm genom exempel? Kort kan man säga att objektorienterat paradigm omfattar imperativt genom att alla det imperativa paradigmets egenskaper återfinns i det objektorienterade och att det imperativa paradigmet i sin tur omfattar det funktionella paradigmets alla egenskaper. Däremot är det deklarativa paradigmet annorlunda genom att man ast anger svarets egenskaper och låter det underliggande runtime -systemet besluta om hur resulltatet skall uppnås. Föreläsning 11 Bild 1 av 19 Föreläsning 11 Bild 2 av 19 Vilka olika metoder har man då tillgång till? Vi har redan berört logikprogrammering. Somliga menar att funktionell programmering är en del av det deklarativa paradigmet men jag personligen tycker inte det. Frågespråk för databashantering däremot, är definitivt deklarativa till sin natur även om det finns inslag av imperativt paradigm i dem. Matematiken för frågespråk kan delas in i tre dicipliner. Relationsalgebra, som är en utvidgning av mängdalgebra, är till sin natur imperativ. Tupelkalkyl, som är en utvidgning av predikatkalkylen, bygger även den på mängder men ligger nära predikatlogik och är alltså deklarativ. Domänkalkyl, som är en annan utvidgning av predikatkalkylen, är även den deklarativ till sin natur. Föreläsning 11 Bild 3 av 19 Föreläsning 11 Bild 4 av 19
Det tupelkalkylbaserade språket SQL anses vara deklarativt men har starka inslag av relationsalgebra (många menar att det är tvärtom). Alla uttryck ger tabeller som resultat: SELECT namn, adress, tel FROM person WHERE typ="anställd" AND avd="sport" så semantiken blir föredömlig enkel och så även matematiken. I tupelkalkyl är alla uttryck på formen alla tupler (tabellrader), sådana att alla villkor uppfyllts, eller: {t ψ(t)} där ψ(t) är en formel som lägger restriktioner på t genom att ange villkor för intressanta egenskaper och relationer till omgivningen, e.g vilka anställda arbetar på 2:a våningen (i ett fiktivt varuhus): {t t anställd ( u)(u avdelning t.avd = u.avd u.vån = 2)} Lägg märke till att det inte finns något imperativ utan bara restriktioner! Alltså deklarativt. Detta leder oss in på den sista disciplinen i det deklarativa paradigmet, restriktionsprogrammering. Typiska programspråk: CLPR (Constraint Logic PRogramming) liknar prolog med bättre stöd för matematiska formler. Man kan bygga hierarkier av restriktioner där den första nivån måste uppfyllas innan man kör programmet och man försöker uppfylla alla krav i en nivå innan man börjar på nästa. Stöd för beräkningar med mer och mer välbestämda förutsättningar. Föreläsning 11 Bild 5 av 19 Föreläsning 11 Bild 6 av 19 OZ, som vi har programmerat funktionellt, imperativt och objektorienterat i. Till skillnad från CLPR så hanteras restriktioner inte genom backtracking utan genom att man ger order om att hitta alla lösningar Ex.: Pusslet S E N D + M O R E --------- M O N E Y har en unik lösning. Hur hittar vi den? Alla restriktioner finns redan i problemets utformning: Låt oss anta att vi ska fylla i en post som innehåller alla symbolerna: Resultat = res(s:s e:e n:n d:d m:m o:o r:r y:y) Ett enkelt resonemang (som jag inte använder i programmet) ger vid handen att M=1 och följdaktligen S=9 och O=0. Men resten av siffrorna? En uppenbar restriktion är att domänen är intervallet 0..9, i oz: Resultat ::: 0#9. Också uppenbart att siffrorna ska vara distinkta, {FD.distinct Resultat}. Dessutom, naturligtvis, kan inte S resp M vara 0 så M \=: 0. S \=: 0 och Föreläsning 11 Bild 7 av 19 Föreläsning 11 Bild 8 av 19
Vidare har vi själva uttrycket som ju säger att 1000 S +100 E +10 N +D + 1000 M +100 O+10 R+E = 10000 M + 1000 O + 100 N + 10 E + Y och slutligen ska vi distribuera siffrorna över vår post: {FD.distribute ff Resultat}, där ff står för strategin first fail. Vi har i o m detta ett program! proc {Money?Resultat} S E N D M O R Y in Resultat = res(s:s e:e n:n d:d m:m o:o r:r y:y) Resultat ::: 0#9 {FD.distinct Resultat} S \=: 0 M \=: 0 1000*S + 100*E + 10*N + D + 1000*M + 100*O + 10*R + E =: 10000*M + 1000*O + 100*N + 10*E + Y {FD.distribute ff Resultat} Föreläsning 11 Bild 9 av 19 Föreläsning 11 Bild 10 av 19 som vi anropar med {Browse {SearchAll Money}} Hur går det till då? Oz finner omedelbart de uppenbara restriktionerna M=1, S=9, O=0 och justerar gränserna för övriga variabler och sedan testar oz: res(s:9 e:4#7 n:5#8 d:2#8 m:1 o:0 r:2#8 y:2#8) e=4? fail res(s:9 e:5#7 n:6#8 d:2#8 m:1 o:0 r:2#8 y:2#8) e=5? == en lösning ==> res(s:9 e:5 n:6 d:7 m:1 o:0 r:8 y:2) e=6? fail res(s:9 e:7 n:8 d:2#8 m:1 o:0 r:2#8 y:2#8) e=7? fail Deklarativt programmeringsparadigm - verktyg i oz Görs också via enkla exempel: Beräkningar baserade på partiell information är det mest grundläggande inom restriktionsprogrammeringen. Man kan dra många slutsatser från tillgänglig information. Antag att vi ska beräkna en yta, A, men vi känner inte till sidorna exakt. Vi vet att 90 x 110 och att 48 y 53 declare X Y in X::90#110 Y::48#53 där X::90#110 egentligen betyder x {90, 91, 92,, 100} Låt oss använda detta för en beräkning Föreläsning 11 Bild 11 av 19 Föreläsning 11 Bild 12 av 19
declare A in A::0#10000 A=:X*Y {Browse A>:4000} {Browse A} oz svarar ja på den första frågan (genom att skriva 1) och A{4320#5830} på den andra. Om vi inte ger gränserna för A kommer oz att blockera svaret tills gränsrestriktionen givits eftersom beräkningar utan gränser potentiellt kan ge oändlig rekursion. Operatorn =: kallas för en propagator, i det här fallet en multiplikation under restriktion. Utan de initiala gränserna för A tillåts inte propagationen. Nu lägger vi till ytterligare en restriktion X-2*Y=:11. Systemet svarar fortfarande att A är större än 4000 och nu ger den svaret A{5136#5341}. Redan med dessa verktyg kan vi göra mycket. Ekvationssystem? Javisst! x y = 24 x + y = 10 Föreläsning 11 Bild 13 av 19 Föreläsning 11 Bild 14 av 19 Blir i oz declare X Y in X::1#9 Y::1#9 X*Y=:24 X+Y=:10 {Browse X} {Browse Y} Svaret? X{4#6}, Y{4#6} Lägger vi till restriktionen X<:Y får vi exakta svaret att X är 4 och Y är 6. declare Res=res(x:X y:y) X::1#9 Y::1#9 X+Y=:5 Y-X=:3 {Browse Res} Svaret? res(x:1 y:4) x + y = 5 y x = 3 Föreläsning 11 Bild 15 av 19 Föreläsning 11 Bild 16 av 19
Relationell programmering I oz kallar man det som vanligtvis kallas logikprogrammering för relationell programmering, vilket (väl?) ger en lite snävare och kanske mer korrekt bild av vad man hittar på i prolog. Resoultion är i allt väsentligt just relationsprogrammering. För att åstadkomma detta har man två språkkonstruktioner: choise s 1 [] [] s n fail Hur fungerar det? Exempel: Relationell programmering... declare fun {Soft} choice beige [] korall fun {Hard} choice lila [] ockra proc {Contrast C1 C2} choice C1={Soft} C2={Hard} [] C1={Hard} C2={Soft} fun {Suit} Shirt Pants Socks in {Contrast Shirt Pants} {Contrast Pants Socks} if Shirt==Socks then fail suit(shirt Pants Socks) Föreläsning 11 Bild 17 av 19 Föreläsning 11 Bild 18 av 19 Relationell programmering... Porgrammet kommer att välja färg på kläder så att man får kontrast mellan skjorta och byxor resp. byxor och strumpor men inte samma färg på skjorta och strumpor. Föt att hitta en lösning kan man använda Solve som tar en parameterlös funktion och bygger ett lösningsträd tills den hittar en lösning. Man kan naturligtvis göra ett litet program som hittar alla lösningar. Föreläsning 11 Bild 19 av 19