Programmergsmetodik DV1 Programkonstruktion 1 Moment 10 Om sidoeffekter In/utmatng och imperativ programmerg Referentiell transparens I ren funktionell programmerg kan man byta lika mot lika utan att programmets funktion ändras. Detta kallas referentiell transparens. Orsaken är att en beräkng te har någon effekt annat än att beräkna ett värde (om man bortser från tids/mnesåtgång). f x*g y kan bytas mot g y*f x. f x ^ f x kan bytas mot val fx = f x fx^fx end Det sista bytet lönar sig resursmässigt om beräkngen av f x tar lång tid eller har ett stort objekt som värde. Ett sådant byte kan t.o.m. göras automatiskt av ML-systemet. PK1&PM1 HT-04 moment 10 Sida 1 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 2 Uppdaterad 2004-10-21 Inmatng Hittills har all /utmatng av data gått via ML-systemets användargränssnitt. I praktiken räcker te det programmen måste själva kunna mata /ut data t.ex. till/från filer. Låt oss anta att det fns en funktion readle med spec.: readle x TYPE : unit -> strg SIDE-EFFECTS: En rad läses från termalfönstret. POST: en sträng med den rad som lästs. EXAMPLE: readle() = "Hej\n" om man skriver raden "Hej\n" på tangentbordet. Körexempel: - readle(); Hej, hopp. > val it = "Hej, hopp.\n" : strg Sidoeffekter Vad blir readle()^readle()? Låt oss anta att användaren först skriver raden "Hej" sedan "hopp". (Varför slutar strängarna med \n?) readle()^readle() > "Hej\n"^readle() > "Hej\n"^"hopp\n" > "Hej\nhopp\n" Observera att readle()!readle()! Man kan alltså te byta readle()^readle() mot val rl = readle() rl^rl end Det beror på att readle te bara beräknar ett värde utan också läser en ny rad från termalen vid varje anrop readle har en sidoeffekt. Anta anrop och deras bördes ordng blir viktig. PK1&PM1 HT-04 moment 10 Sida 3 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 4 Uppdaterad 2004-10-21 Sekvenserg När sidoeffekter fns blir beräkngsordngen mycket viktig. Tänk på att ML beräknar uttryck från vänster till höger (och ifrån och ut). Ibland kan man vilja beräkna flera uttryck efter varandra endast för deras sidoeffekter utan att bry sig om värdet. För detta ändamål fns ML-konstruktionen e1 ; e2 ;... ; en-1 ; en vars värde är värdet av en, men som dessutom först beräknar e1 t.o.m. en-1. e1;e2;...;en-1;en är mengsfull bara om beräkngen av e1 t.o.m. en-1 har sidoeffekter. (Obs att ; även avslutar uttryck som man matar till ML. ; som sekvensoperator måste stå om parenteser,..end eller dylikt.) PK1&PM1 HT-04 moment 10 Sida 5 Uppdaterad 2004-10-21 Utmatng prt x : strg -> unit SIDE-EFFECTS: x skrivs ut på termalfönstret. EXAMPLE: prt "Hej\n" = () och skriver samtidigt raden "Hej\n" på termalfönstret. prt "Hej\n";prt "Hej\n" > ();prt"hej\n" (och "Hej\n" skrivs ut.) > ();() (och "Hej\n" skrivs ut.) > () Alltså prt "Hej\n" = prt "Hej\n" trots det kan man te byta prt "Hej\n";prt "Hej\n" mot val ph = prt "Hej\n" ph;ph end prt beräknar te bara ett värde utan skriver också på termalen. En ny rad skrivs vid varje anrop prt har en sidoeffekt. Anta anrop och deras bördes ordng blir viktig. PK1&PM1 HT-04 moment 10 Sida 6 Uppdaterad 2004-10-21
In/utmatng i ML TextIO För /utmatng (I/O) fns olika bibliotek i ML, bl.a. biblioteket TextIO för - och utmatng av text. För att slippa skriva TextIO. före alla namn på funktioner och värden i detta bibliotek kan man använda deklarationen open TextIO; i sitt program. Den gör att ML automatiskt söker i biblioteket TextIO efter namn den te känner igen annars. (Detta fungerar med alla bibliotek, även t.ex. Strg och List.) Man kan använda flera open-deklarationer. Jag förutsätter i fortsättngen att man använt deklarationen open TextIO. (Obs! open är gen ersättng för load.) PK1&PM1 HT-04 moment 10 Sida 7 Uppdaterad 2004-10-21 Strömmar Vid I/O krävs omfattande admistration, bl.a. skall filer lokaliseras och man behöver hålla reda på hur långt i en fil man har läst/skrivit. Läspekare frithiof.txt: Der växte uti Hildgs gård två plantor under fostrarns vård. Ej Norden förr sett två så sköna, de växte härligt i det gröna. TextIO fns abstrakta datatyper stream och outstream kallade strömmar. All formation rörande I/O fns i strömmarna. För I/O till termalfönster fns i TextIO två fördefierade strömmar stdin av typ stream och stdout av typ outstream. PK1&PM1 HT-04 moment 10 Sida 8 Uppdaterad 2004-10-21 Skapa strömmar openin s TYPE: strg -> stream PRE: s skall vara namnet på en fil som fns. POST: En stream kopplad till filen s. SIDE-EFFECTS: Olika anrop till openin ger olika strömmar. EXAMPLE : openin "frithiof.txt" openout s : strg -> outstream PRE: s skall vara namnet på en fil som går att skriva/skapa. POST: En outstream kopplad till filen s. SIDE-EFFECTS: Filen s skapas om den te fns. Olika anrop till openout ger olika strömmar. EXAMPLE : openout "nyfil" PK1&PM1 HT-04 moment 10 Sida 9 Uppdaterad 2004-10-21 Ett urval funktioner för matng (1) putle is TYPE: stream -> strg POST: Nästa rad från strömmen is (kl. \n), eller tom sträng om get kunde läsas (t.ex. filslut) SIDE-EFFECTS: Läspekaren för is flyttas fram. Exempelfunktionen readle kan defieras så här: fun readle() = putle stdin; put1 is TYPE: stream -> char option POST: SOME c om nästa tecken i strömmen är c, NONE om get kunde läsas (t.ex. filslut). SIDE-EFFECTS: Läspekaren för is flyttas fram. closein is TYPE: stream -> unit. SIDE-EFFECTS: Avslutar användngen av strömmen is PK1&PM1 HT-04 moment 10 Sida 10 Uppdaterad 2004-10-21 Ett urval funktioner för matng (2) putall is TYPE: stream -> strg POST: Resterande text i strömmen is till filslut. SIDE-EFFECTS: Läspekaren för is flyttas fram. endofstream is TYPE: stream -> bool POST: true om läsngen av strömmen is har nått till slutet av filen, false annars. SIDE-EFFECTS: ga. PK1&PM1 HT-04 moment 10 Sida 11 Uppdaterad 2004-10-21 Exempel på läsng från ström (* readtolist f TYPE: strg -> strg list PRE: f är namnet på en fil som kan läsas POST: En lista av raderna i filen *) fun readtolist f = (* readtolistaux is TYPE: Stream -> strg list POST: En lista av återstående rader i is *) (* VARIANT: återstående rader i is *) fun readtolistaux is = if endofstream is then (closein is; []) putle is :: readtolistaux is readtolistaux (openin f) PK1&PM1 HT-04 moment 10 Sida 12 Uppdaterad 2004-10-21
Termerg av rekursion vid läsng När man läser data från en ström i ett rekursivt program så bryts i allmänhet rekursionen av filslut. Man kan te veta i förväg hur många rekursiva anrop som skall ske utan att veta hur strömmen ser ut. Det vet man normalt te nan den är läst! Desutom är det prciellt omöjligt om man läser från en ström som skapas successivt (t.ex. en ström som läser från ett tangentbord.) Som rekursionsvariant kan man ange t.ex. (* VARIANT: oläst längd på strömmen is *) Om funktionen läser från is och rekursionen avslutas vid filslut. PK1&PM1 HT-04 moment 10 Sida 13 Uppdaterad 2004-10-21 Två vanliga fel... (* readtolist f TYPE: strg -> strg list PRE: f är namnet på en fil som kan läsas POST: En lista av raderna i filen *) (* VARIANT: återstående rader i f *) fun readtolist f = val is = openin f if endofstream is then [] putle is :: readtolist f) readtolist öppnar en ny ström vid varje anrop och läser därför första raden i filen om och om igen. Inga strömmar stängs någons. PK1&PM1 HT-04 moment 10 Sida 14 Uppdaterad 2004-10-21 Ett urval funktioner för utmatng (1) output(os,s) TYPE: outstream*strg -> unit SIDE-EFFECTS: Strängen s skrivs ut på strömmen os. Ett urval funktioner för utmatng (2) output1(os,c) TYPE: outstream*char -> unit SIDE-EFFECTS: Tecknet c skrivs ut på strömmen os. flushout os TYPE: outstream -> (). SIDE-EFFECTS: Skriver ut bufferten för strömmen os closeout os TYPE: outstream -> unit. SIDE-EFFECTS: Avslutar användngen av strömmen os Den byggda funktionen prt skulle kunnat defieras så här: fun prt s = (output(stdout,s);flushout stdout); PK1&PM1 HT-04 moment 10 Sida 15 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 16 Uppdaterad 2004-10-21 Tillägg till beftliga filer openout skapar en ny fil eller skriver över en beftlig fil. Man kan också vilja lägga till ny formation i slutet av en beftlig fil. openappend s TYPE: strg -> outstream PRE: s skall vara namnet på en fil som går att skriva elller skapa. POST: En outstream kopplad till filen s. SIDE-EFFECTS: Filen s skapas om den te fns. Skrivngen börjar i slutet av filen. Olika anrop till openappend ger olika strömmar. Tillägg till filer "exempel Om vi har en fil testut.txt, med nehåll: Hej, Hopp I det gröna och gör: - val x = openappend "testut.txt"; > val x = <outstream> : outstream - output(x,"sade Frithiof\n"); - closeout x; så kommer testut.txt att nehålla: Hej, Hopp I det gröna sade Frithiof PK1&PM1 HT-04 moment 10 Sida 17 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 18 Uppdaterad 2004-10-21
I/O av annat än text Vill man mata /ut data av annan typ än strg måste man omvandla den till till/från strängar först. Omvandlgen måste man programmera själv om te byggd funktion fns (t.ex. Int.toStrg) Konverterg från strängar är besvärligt. I allmänhet har strängen olika typer av formation blandade efter varandra, t.ex: "beräkna 1+23*4 slut". Ur denna sträng skulle man vilja få: strängen "beräkna", ta 1, tecknet +, ta 23, tecknet *, ta 4, och strängen "slut". Dessutom vill man kanske ha 1+23*4 representerat som ett träd! Detta problem kallas syntaxanalys eller parsng. Parsng behandlas i senare kurser. PK1&PM1 HT-04 moment 10 Sida 19 Uppdaterad 2004-10-21 Metodik för utvecklg av program med i/o Tänk noga på i vilken ordng saker och tg utförs Försök dela upp programmet i en del som utför all /utmatng och en annan del som te alls utför någon i/o. Testa alla data som läses lita te på att de är riktiga! För teraktiva program (program som arbetar i dialog med en användare) bör man dessutom tänka på: Programmet får te kunna avbrytas pga fel så att användaren förlorar data. Tänk igenom dialogen med användaren strukturera programmet efter den. PK1&PM1 HT-04 moment 10 Sida 20 Uppdaterad 2004-10-21 En kalkylator i ML Skriv en enkel fyrfunktionskalkylator i ML! Den skall läsa ett tal, en operation och ett tal till och visa resultatet. Därefter skall man kunna utföra kedjeräkng med nya operationer och tal. Skriv ett tal: 3 Skriv ett tal: 3.5 Skriv en operation: + Skriv en operation: d Skriv ett tal: 4 Felaktig operation! Resultatet är: 7.0 Skriv en operation: / Skriv en operation: + Skriv ett tal: 0 Skriv ett tal: 5.2 Fel! Resultatet är: 12.2 Skriv ett tal: 0 Skriv en operation: c Skriv en operation: q Skriv ett tal: abc Felaktigt tal! PK1&PM1 HT-04 moment 10 Sida 21 Uppdaterad 2004-10-21 Tillståndsdiagram När man planerar programmet kan man göra ett tillståndsdiagram över dialogen. Avsluta Börja Läs ett tal Läs en operation Skriv ut resultatet Läs ett tal och beräkna Programmet skrivs som en tillståndsmask som motsvarar diagrammet. Varje tillstånd motsvaras av en funktion i programmet. PK1&PM1 HT-04 moment 10 Sida 22 Uppdaterad 2004-10-21 Programstrukturen Programkoden visas i top-down ordng med huvudfunktionerna först och hjälpfunktionerna sist. I programfilen måste funktionerna ligga i motsatt ordng eftersom en funktion måste defieras nan de används. Funktionerna i tillståndsmasken är ömsesidigt rekursiva. Programmet leds med deklarationerna: load "Real"; open TextIO; PK1&PM1 HT-04 moment 10 Sida 23 Uppdaterad 2004-10-21 Tillståndsmasken (1) (* calc x TYPE: unit -> unit SIDE-EFFECTS: Huvudfunktion i kalkylatorn *) fun calc() = readop(readnum()) (* readop x TYPE: real -> unit SIDE-EFFECTS: Läser en operation i kalkylatorn *) and readop x = (prt "Skriv en operation: " ; case putle stdin of "q\n" => () "c\n" => calc() opr => case selectop opr of SOME f => doop(x,f) NONE => (prt "Felaktig op.\n"; readop x)) PK1&PM1 HT-04 moment 10 Sida 24 Uppdaterad 2004-10-21
Tillståndsmasken (2) (* doop(x,f) TYPE: real*(real*real -> real) -> unit SIDE-EFFECTS: Läser en tal och utför en operation i kalkylatorn *) and doop(x,f) = (prtresult(f(x,readnum())) handle _ => (prt "Fel!\n"; calc())) (* prtresult x TYPE: real -> unit SIDE-EFFECTS: Skriver resultat i kalkylatorn *) and prtresult x = (prt ("Resultatet är: " ^ Real.toStrg x ^ "\n"); readop x); Välj operation (* selectop x TYPE: strg -> (real*real -> real) option POST: Om x är namnet på en känd operation returneras SOME f, där f är en funktion som utför operationen. NONE annars. EXAMPLE: selectop "+\n" = SOME (op +) *) fun selectop "+\n" = SOME (op +) selectop "-\n" = SOME (op -) selectop "*\n" = SOME (op * ) selectop "/\n" = SOME (op /) selectop _ = NONE; PK1&PM1 HT-04 moment 10 Sida 25 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 26 Uppdaterad 2004-10-21 Läs tal (* readnum x TYPE: unit -> real SIDE-EFFECTS: Läser från termalen POST: Ett läst flyttal *) fun readnum () = (prt("skriv ett tal: "); case Real.fromStrg (putle stdin) of SOME x => x NONE => (prt "Felaktigt tal\n"; readnum ())); PK1&PM1 HT-04 moment 10 Sida 27 Uppdaterad 2004-10-21 Återblick: gentg ändras... I ren funktionell programmerg kan man aldrig ändra någontg bara skapa nytt. Detta är en förutsättnig för att byta lika mot lika. - val x = [1,2,3,4]; > val x = [1, 2, 3, 4] : t list - rev x; > val it = [4, 3, 2, 1] : t list - x; x är oförändrad efter > val it = [1, 2, 3, 4] : t list anropet av rev x! - val y = 1; > val y = 1 : t - fun f x = x+y; > val f = fn : t -> t - val y = 2; > val y = 2 : t - f 2; f använder den gamla > val it = 3 : t bdngen av y! PK1&PM1 HT-04 moment 10 Sida 28 Uppdaterad 2004-10-21 Modifierbara celler ML ger möjlighet att skapa datastrukturer som verkligen kan ändras. ref är en datatypskonstruktor som nästan beter sig som datatype 'a ref = ref of 'a; ref 3 kallas en referens till värdet 3. (Kallas också cell, box, låda). För att komma åt refererade värden använder man funktionen! fun! (ref x) = x;!x kallas derefererg av x. Referenser kan ändras med den fixa funktionen :=: r := v TYPE: 'a ref*'a -> unit SIDE-EFFECTS: Värdet i r ändras till v. EXAMPLE: val x = ref 3;!x = 3 x := 2; Nu är!x = 2 Ingentg är säkert längre... Använder man referenser så kan man i allmänhet te längre byta lika mot lika. - val y = ref 1; > val y = ref 1 : t ref y är nu bunden till en referens till ett heltal. - val =!y; > val = 1 : t - fun f x = x+!y; Obs. blank mellan +! > val f = fn : t -> t - y := 2; - =!y; y är nu ändrad > val it = false : bool - f 2; f använder det nya > val it = 4 : t värdet! PK1&PM1 HT-04 moment 10 Sida 29 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 30 Uppdaterad 2004-10-21
Ändrgar kan ske bakom ryggen Anrop av funktioner med sidoeffekter kan förändra data beräkngsordngen blir kritisk. (* bump x TYPE: t ref -> t SIDE-EFFECTS: Ökar nehåller i cellen x med 1. POST: Det nya värdet av x. *) fun bump x = (x :=!x + 1;!x); - val x = ref 1; > val x = ref 1 : t ref - bump x; > val it = 2 : t -!x; > val it = 2 : t Obs att (bump x)+!x och!x+(bump x) beräknar olika värden! PK1&PM1 HT-04 moment 10 Sida 31 Uppdaterad 2004-10-21 Bdngsomgivngar och lagret Defitioner (val, fun) bder identifierare till värden. Bdngar kan aldrig ändras. Bdngsomgivngar är en samlg bdngar. Bdngsomgivngar kan skapas och försvna. Dessutom har ML ett lager (eng. store) som är en samlg celler. ref skapar celler som nehåller ett värde Innehål i celler kan ändras Lagret är en samlg celler Det fns bara ett lager och det försvner aldrig. - val y = ref 1; > val y = ref 1 : t ref y 1 PK1&PM1 HT-04 moment 10 Sida 32 Uppdaterad 2004-10-21 Likhet och olikhet hos celler - val y = ref 1; > val y = ref 1 : t ref - val y' = y; > val y' = ref 1 : t ref - y := 2; - val = ref 2; > val = ref 2 : t ref - y = y'; > val it = true : bool - y = ; > val it = false : bool y y' 2 y 1 y y' y y' 1 2 2 PK1&PM1 HT-04 moment 10 Sida 33 Uppdaterad 2004-10-21 Referenser från celler till celler - val x = ref 1; > val x = ref 1 : t ref - val y = ref 2; x 1 > val y = ref 2 : t ref - val = ref x; y 2 x 1 > val = ref ref 1 : t ref ref x 1 - val u = ref (!x); > val u = ref 1 : t ref y 2 u 1 y 2 får typ t ref ref en pekare till en annan cell! x 1 PK1&PM1 HT-04 moment 10 Sida 34 Uppdaterad 2004-10-21 Referenser från celler till celler (forts.) x 1 -! := 3; u 1 y 2 x 3 - := y; u 1 - := ref 4; u 1 u 1 y 2 x 3 y 2 x 3 y 2 4 PK1&PM1 HT-04 moment 10 Sida 35 Uppdaterad 2004-10-21 - := u; Skräpsamlg u 1 u 1 x 3 x 3 y 2 4 y 2 Cellen med värdet 4 är te åtkomlig längre den har blivit skräp. Skräp tas bort automatiskt med skräpsamlg (garbage collection). Alla sorters data i ML kan bli skräp te bara celler utan t.ex. listor men fenomenet märks kanske tydligast med celler. Tack vare skräpsamlgen fungerar det att ML-program arbetar med att ständigt konstruera nya objekt utan att någons ta bort gamla. PK1&PM1 HT-04 moment 10 Sida 36 Uppdaterad 2004-10-21 4
Imperativ programmergsstil I imperativ programmergsstil arbetar man med att ändra nehål i celler snarare än bda identifierare. utföra beräkngar i steg (satser) som har sidoeffekter, snarare än som uttryck. (Enkla uttryck är ofta delar av satser.) utföra upprepngar med loopar snarare än rekursion. Imperativ programmergsstil är det normala i programspråk som Pascal, C, Java... Det som kallas variabler i dessa språk är normalt namngivna ref-celler. Uttryck av ref-typ i ML motsvarar Lvalues i C alltså något som kan tilldelas ett värde. C och andra imperativa språk derefererar (utför!-operationen på) variabler automatiskt när det behövs. PK1&PM1 HT-04 moment 10 Sida 37 Uppdaterad 2004-10-21 Ett imperativt program (* abssqrt x TYPE: real->real POST: Kvadratroten av absolutbeloppet av x. *) fun abssqrt(x) = val x' = ref x if!x' < 0.0 then x' := ~ (!x') () ; < ; skiljer uttryck som fungerar som satser. Math.sqrt(!x') PK1&PM1 HT-04 moment 10 Sida 38 Uppdaterad 2004-10-21 Loopar En loop upprepar ett uttryck (sats). while e1 do e2 Uttrycket e2 beräknas upprepade gånger så länge uttrycket e1 beräknas till true. Värdet av while är alltid (). Utan sidoeffekter är detta mengslöst eftersom e1 och e2 alltid skulle ge samma värden! Detta uttryck nehåller en loop som skriver ut Hej igen! 10 gånger: val i = ref 1 while!i<=10 do (prt "Hej igen!\n"; i :=!i + 1) PK1&PM1 HT-04 moment 10 Sida 39 Uppdaterad 2004-10-21 Analys av summergsproblemet (repris) Start. Låt summan vara 0. Är n=0? Nej. Ja. Addera n till summan. Subtrahera 1 från n. Stopp. Konstruktionen kallas för en loop. (Det spelar gen roll om vi adderar n+...+0 eller 0+...+n) PK1&PM1 HT-04 moment 10 Sida 40 Uppdaterad 2004-10-21 Imperativ summergsfunktion (* sumupto TYPE: t -> t PRE: x >= 0 POST: 1+2+...+x *) fun sumupto x = val n = ref x val sum = ref 0 while!n > 0 do (sum :=!sum +!n; n :=!n - 1);!sum Samma funktion i funktionell variant: fun sumupto n = if n = 0 then 0 n + sumupto(n-1); PK1&PM1 HT-04 moment 10 Sida 41 Uppdaterad 2004-10-21 Imperativ genomgång av listor (* countgreater(x,g) TYPE: t list*t -> t POST: Antal element i x som är större än g *) fun countgreater(x,g) = val l = ref x val n = ref 0 while not (null(!l)) do (if hd(!l) > g then n :=!n + 1 (); l := tl(!l));!n PK1&PM1 HT-04 moment 10 Sida 42 Uppdaterad 2004-10-21
countgreater imperativ och funktionell fun countgreater(x,g) = (* imperativ *) val l = ref x val n = ref 0 while not (null(!l)) do (if hd(!l) > g then n :=!n + 1 (); l := tl(!l));!n fun countgreater(l,g) = (* funktionell *) if null l then 0 (if hd l > g then 1 0) + countgreater(tl l,g) PK1&PM1 HT-04 moment 10 Sida 43 Uppdaterad 2004-10-21 Loopar och svansrekursion (* Med loop *) fun sumupto x = val n = ref x val sum = ref 0 while!n > 0 do (sum :=!sum +!n; n :=!n - 1);!sum (* Med svansrekursion *) fun sumupto x = sumuptoaux(x,0); fun sumuptoaux(n,sum) = if n > 0 then sumuptoaux(n-1,sum+n) sum PK1&PM1 HT-04 moment 10 Sida 44 Uppdaterad 2004-10-21 Arrayer För att komma åt ett element i en lista eller ett träd krävs en tidsåtgång som ökar när datastrukturen blir större. Man har ibland behov av att komma åt data i konstant tid. Nästan alla datorer har arbetsmne med sekventiellt ordnade celler där olika delar går att komma åt i konstant tid. De flesta programspråk har en datastruktur som direkt motsvarar strukturen hos mnet arrayen (vektor, fält). 1.0 4.2 6.1 3.1 10.0~3.0 0 1 2 3 4 5 En flyttalsarray med 6 element och deras dex. Till skillnad från en lista som består av ett antal ::-konstruktorer är en array ett objekt att komma åt alla element går alltså lika fort. Arrayer i ML Arrayer i ML defieras i biblioteket Array och liknar en abstrakt datatyp. Det är dock en likhetstyp. (Men observera att liksom hos ref så kan två arrayer vara olika även om nehål är lika!) 1.0 4.2 6.1 3.1 10.0~3.0 0 1 2 3 4 5 Arrayen ovan har i ML typen real array. Liksom ref-celler kan nehål i arrayer ändras. Arrayer skiljer sig från ref i prcip genom att ref är en cell, medan arrayer normalt har flera celler. PK1&PM1 HT-04 moment 10 Sida 45 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 46 Uppdaterad 2004-10-21. Skapa arrayer Array.fromList x TYPE: 'a list -> 'a array POST: En array av samma längd som listan där cellerna i tur och ordng nehåller elementenn i listan SIDE-EFFECTS: En ny array skapas. Array.array(n,x) TYPE: t*'a -> 'a array PRE: n >= 0 POST: En array av längd n där cellerna nehåller x SIDE-EFFECTS: En ny array skapas. Använda arrayer Array.length a TYPE: 'a array -> t POST: Antal celler i arrayen a Array.sub(a,i) TYPE: 'a array*t -> 'a PRE: 0 <= i < length a POST: Innehål hos cellen med dex i om arrayen a. Array.update(a,i,x) TYPE: 'a array*t*'a -> unit PRE: 0 <= i < Array.length a SIDE-EFFECTS: I arrayen a sätts nehål i cellen med dex i till x. PK1&PM1 HT-04 moment 10 Sida 47 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 48 Uppdaterad 2004-10-21
Högre ordngens funktioner för arrayer Array.foldr f e a TYPE: ('a*'b->'b)->'b->'a array->'b POST: f(x1,f(x2,...f(xn,e)...)) om arrayen a nehåller elementen x1,x2,...,xn Jämför med specifikationen av foldr för listor: foldr f e l TYPE: ('a*'b->'b)->'b->'a list->'b POST: f(x1,f(x2,...f(xn,e)...)) om l=[x1,x2,...,xn] Array.foldr kan användas för att defiera en funktion tolist som är bra att ha för att se hur arrayer ser ut. (* tolist a TYPE: 'a array -> 'a list POST: En lista av elementen i arrayen a. *) fun tolist a = Array.foldr (op ::) [] a; Exempel på arrayfunktioner - val a = Array.fromList [10,20,30,40]; > val a = <array> : t array - tolist a; > val it = [10, 20, 30, 40] : t list - Array.length a; > val it = 4 : t - Array.sub(a,3); > val it = 40 : t - Array.update(a,2,42); - tolist a; > val it = [10, 20, 42, 40] : t list - val b = Array.array(3,0); > val b = <array> : t array - tolist b; > val it = [0, 0, 0] : t list PK1&PM1 HT-04 moment 10 Sida 49 Uppdaterad 2004-10-21 PK1&PM1 HT-04 moment 10 Sida 50 Uppdaterad 2004-10-21 Summera elementen i en flyttalsarray Funktionellt: (* sumarray a TYPE: real array -> real POST: Summan av elementen i a *) fun sumarray a = (* sumarrayaux(a,i) TYPE: real array*t -> real POST: Summan av elementen i a fr.o.m. elementet med dex i *) fun sumarrayaux(a,i) = if i >= Array.length a then 0.0 Array.sub(a,i) + sumarrayaux(a,i+1) sumarrayaux(a,0) PK1&PM1 HT-04 moment 10 Sida 51 Uppdaterad 2004-10-21 Summera elementen i en flyttalsarray Imperativt: (* sumarray a TYPE: real array -> real POST: Summan av elementen i a *) fun sumarray a = val sum = ref 0.0 val i = ref 0 while!i < Array.length a do (sum :=!sum + Array.sub(a,!i); i :=!i + 1);!sum Bekvämt: fun sumarray a = Array.foldr (op +) 0.0 a PK1&PM1 HT-04 moment 10 Sida 52 Uppdaterad 2004-10-21