Introduktion till formella metoder Programmeringsmetodik 1. Inledning Fokus på imperativa program (ex. C, Java) program betyder härefter ett imperativt program Program bestäms i en abstrakt mening av hur värdena på dess programvariabler ges av tilldelningar som i sin tur styrs av programsatser (som beskriver vad programmeraren har gjort (också utnyttjande givna färdiga procedurer (funktioner) som antas givna). P är ett program V(P) = (v 1, v 2, v n-1, v n ) = programvariablerna v i i P (där i = 1,2,3, n) ordnade i en följd för variablerna v i antar värden i sina värdemängder A i P kan vara i olika tillstånd. Tillstånden är entydigt bestämda av värdena på variablerna: (a 1, a 2, a n-1, a n ) A 1 A 2 A n-1 A n ett tillstånd σ (state) tillståndsrummet Σ (state space) mängden av alla tillstånd (alla behöver inte förverkligas för P) Obs: Programmets tillståndsrum ger en mycket grov beskrivning av datorns tillståndsrum (på elektroniknivå).
Tillståndsrummet Σ = A 1 A 2 A n-1 A n Starttillstånd = σ 0 S 1 σ 1 σ m = Sluttillstånd σ 2 S 2 (Sluttillstånd kan saknas) S 3 σ 3 Programmet P motsvaras av följden (σ 0, σ 1,, σ m ) av tillstånd. [ Jfr. automater? ändliga eller oändliga? ] Följden (σ 0, σ 1,, σ m ) bestäms av tillståndsrummet Σ starttilltåndet σ 0 transitionerna σ i-1 σ i som bestäms av det aktuella tillståndet σ i-1 och programsatser S i, för i = 1,2,, m [ programsatserna och tillstånden kan vara obegränsat många! ] Följden (σ 0, σ 1,, σ m ) ger semantiken för P, som visar vad exekveringen av P egentligen innebär. Programsatserna S i ger här en abstrakt version av programmet sådant det är skrivet på något (dugligt) programmeringsspråk.
Programsatserna S i utgör speciella uttryck av andra uttryck och variabler och (namn på) dessas värden samt vissa sammanfogande operationer. Programsatserna (och uttrycken däri) skall ha en bestämd syntax (grammatik) så att de kan behandlas formellt (delvis också av datorn själv, automatisk programverifiering, etc.). Syntaxen bestäms av primitiva tecken och tillåtna kombinationer av dessa vilka för enskilda uttryck kan uttryckas t.ex. med en trädstruktur. Exempel 1. Uttryck (aritmetiskt): -2 * x + 3 Trädsyntax: Parentessyntax : Primitiva tecken: 2, 3, x, *, -, + 2, 3, x, *, -, +, (, ) + * 3 - x 2 Jfr. Uttryck (aritmetiskt): -2 * (x + 3) ((-2 * x) + 3) Trädsyntax: Parentessyntax : Primitiva tecken: 2, 3, x, *, + 2, 3, x, *, +, (, ) (parenteserna hjälptecken ovan) * - + 2 x 3 (-2 * (x +3))
Exempel 2. Uttryck (booleskt): 0 p 1 Trädsyntax: Parentessyntax : Primitiva tecken: 0,1, p,,, 0,1, p,,,, (, ) (( 0 p) 1) 1 p 0 Jfr. Syntax: Polsk notation Omvänd polsk notation Primitiva tecken: 0,1, p,,, 0,1, p,,,, ; 0p1 0 ; p ;1 Själva den abstrakta trädstrukturen kan vara densamma för olika uppsättningar av primitiva tecken. Den abstrakta strukturen ges av grammatiken (som kan vara samma för språk med olika ordförråd).
Abstrakt syntax: [jfr. kursen i fundamentala strukturer] Syntaktiska kategorier (med namn och metavariabler för varje kategori) [ jfr nonterminaler i grammatiker ] Syntaktiska regler (för varje kategori) [ jfr produktioner i grammatiker ] Exempel 3. Abstrakt syntax för aritmetiska och logiska uttryck (som kan ingå i programsatser) Syntaktiska kategorier (t.ex.): heltal namn: int, metavariabler: n (n, n 1,.) heltalsvariabler namn: var, metavariabler: v (v,v 1, ) aritmetiska uttryck namn: arith, metavariabler: e (e,e 1, ) logiska (booleska) uttryck namn: bool, metavariabler: b (b,b 1, ) Syntaktiska regler: e :: = n v e + e e * e [motsv: e n v e + e e * e ] b :: = e = e not b b and b [ b e = e not b b and b ] Syntaktiska objekt: De (ändliga) trädstrukturer som den abstrakta syntaxen bestämmer [ jfr genereringsträd] Obs: (a) I kompendiet står det t.ex. e ::= n v e + e e * e för att utmärka att uttrycken kan vara olika (men samma kan antas gälla utan att tillägga e ). (b) (Heltals)variabler och heltal ses här som atomära och ges ingen inre struktur i denna grammatik. Men man kunde tänka sig en annan där man gav också dem en inre struktur.
Programsatserna (i ett abstrakt programmeringsspråk som tar fasta bara på det väsentliga) Programsatsernas syntax: Syntaktiska kategorier: programsatser namn: (saknas), metavariabler: S (S, S 1,.) programvariabler namn: var, metavariabler: V,(V, V 1, ) uttryck namn: arith, bool, etc, metavariabler: E,(E, E 1, ) logiska uttryck namn: bool, metavariabler: B (B,B 1, ) operationer := abort skip ; if then else fi do od Syntaktiska regler: S :: = abort skip V := E S ; S if B then S else S fi do B S od [ S abort skip V := E S ; S if B then S else S fi do B S od ] Obs: Inbyggd i programsatsernas syntax finns syntaxen för andra uttryck, bl.a. logiska uttryck (jämte eventuell utvidgningar av syntaxen med definitioner). Jfr. datatyper. Syntaktiska objekt: De (ändliga) trädstrukturer som den abstrakta syntaxen bestämmer.
Intuitiv semantik (betydelseguide) för programsatserna := x := E betyder att värdet på uttrycket E beräknas i det aktuella tillståndet σ och variabeln x får sedan detta värde i nästa tillstånd σ. (Obs: E används både som beteckning för uttrycket som för dess beräknde värde här). värdet på E beräknas värdet på E värdet på E {x = a, E} x := E {x = E} σ σ tilldelning: x := E abort misslyckad transition (misslyckat program, krasch) skip noll-transition, ingen ändring av tillstånd (och programvariabler): ; S ; S betyder att S exekveras först och sedan S S S σ σ σ S ; S sekvens: S ; S if B then S else S värdet på det logiska uttrycket B beräknas i tillståndet σ, om värdet är true (= sant = 1), så exekveras S, om värdet är false (= osant = 0), så exekveras S. ( B= true) S σ σ eller ( B= false) S villkorsats: σ σ if B then S else S
do B S od betyder att värdet på det logiska uttrycket B beräknas i tillståndet σ. Om B= true, så exekveras S. Sedan beräknas B i det nya tillståndet σ 1. Om B= true där, så exekveras S. Sedan beräknas B i det nya tillståndet σ 2, etc., tills B = false, eller om inte, så fortsätter exekveringen av S utan ände. {B= true} S {B= true} S {B= true} S σ σ 1 σ 2 {B= true} S {B= false} σ n-1 σ n slinga: do B S od Tillägg till språket: Multipla (samtidiga) tilldelningar x,y := E,E betyder samma som x := E ; y := E ifall inte y ingår i E och/eller x i E, annars ej. Exempel 4: x,y := y,x betyder: x := y ; y := x betyder: {x = a y = b} x,y := y,x {x = b y = a} σ σ {x = a y = b} x := y {x = b y = b} y := x {x = b y = b} σ σ 1 σ 2 Uppg: Hur kan man utföra variabelbytet x,y := y,x utan multipla tilldelningar?
Datatyper: En programvariabel v är alltid av någon datatyp, låt oss allmänt kalla den typ.vi skriver då v : typ. Denna datatyp bestämmer vad den tänkta datorn gör och kan göra med programvariabeln och vad vi som programmerare kan utnyttja av den i datorn nedlagda kapaciteten då vi skriver programmet, t.ex. vilka värden och andra egenskaper variabeln v då har och vilka operationer och relationer som datorn kan identifiera och hantera. Exempel: Om v är av typen naturligt tal, kort num, så är de värden som kommer ifråga de naturliga talen (och inget annat) och operationerna: addition av två naturliga tal, + multiplikation av två naturliga tal, * division av två naturliga tal, div (där divisorn är olika 0) bestämning av rest vid division: mod ( div ) (där divisorn är olika 0) o.s.v.?! egenskaperna (de enställiga relationerna) jämna naturliga tal udda naturliga tal o.s.v.!? relationerna: = likhet olikhet (större än eller lika med) > olikhet (större än) o.s.v.?! Här föreligger för oss en viss obestämdhet rörande vilka operationer som vi kan utgå att finns nedlagda från början. Hur är det t.ex. med subtraktion? Om vi har den med så bör vi också beakta att subtraktionen är definierad för de naturliga talen ifråga, d.v.s. att minuenden är större än eller lika med subtrahenden. Och hur är det med potensering? Hur är det med egenskaper sådana som primtal och relationer som de omvända olikheterna, delbarhet? Om de inte finns färdiga, så kan vi (men inte alltid) införa dem genom att skriva program för dem utnyttjande de operationer och relationer som redan finns.
Lista över vissa datatyper: (namn, värdemängd, uttryck (inkl. variabler och syntax)) En samling datattyper som vi kommer att använda: ( bool, { true, false }, logiska uttryck ) vanliga booleska operationer ( num, { 0, 1, 2, }, aritmetiska uttryck för naturliga tal) ( int, { 0, ±1, ±2, }, aritmetiska uttryck för heltal) vanliga operationer, men * för multiplikation, och div för division och mod för resten vid division. ( list,{ l l en lista av värden av samma typ}, uttryck för listor) operationer: hd(l) = första elementet i l tl(l) = resterande listan då första elementet har avlägsnats. x :: l = l där hd(l ) = x och tl(l ) = l l@ l = listan som fås då l och l fogas ihop till en lista (i denna ordning). [] = tomma listan Beteckning: Ex. [4,2,3,0] = listan med värdena 4, 2, 3 och 0 i denna ordning. ( set,{ a a är värden av samma typ}, uttryck för mängder) vanliga mängdoperationer ( array,{ a a är arrayer av värden av samma typ}, uttryck för arrayer) operationer: a[i] som ur arrayn a tar ut (elementet på) plats i, där i = 0, 1, 2, n 1 och n är antalet platser (element) i a. Beteckning: Ex. array[n] of set och array[n] of int, där n är något naturligt tal. a[i..j] och a[i..j] = x för elementen fr.o.m. a[i] till a[j] resp. påståndet att alla dessa är = x. Typbestämning av variabel betecknas: Ex. b : bool, x : num, och a : array[n] of int