"Referentiell transparens"

Relevanta dokument
Programkonstruktion. Moment 11 Om sidoeffekter In/utmatning och imperativ programmering. PKD 2011/12 moment 11 Sida 1 Uppdaterad

Programkonstruktion. Tentamen,

Programkonstruktion. Tentamen,

Programkonstruktion. Tentamen,

Referentiell transparens. Programkonstruktion. Moment 10 Om sidoeffekter In/utmatning och imperativ programmering. Sidoeffekter.

Programmeringsmetodik DV1, Programkonstruktion del 1 Tentamen,

Referentiell transparens. Programmeringsmetodik DV1 Programkonstruktion 1. Moment 10 Om sidoeffekter In/utmatning och imperativ programmering

DD1361 Programmeringsparadigm. Carina Edlund

Webbsidor och webbservrar

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

Programkonstruktion och datastrukturer. Formell verifiering eller hur man bevisar att program gör rätt utan att testa dem

Python. Python är, som Scheme, ett interpreterat språk men det finns kompilatorer för Python.

Föreläsning 2 Programmeringsteknik och C DD1316. Mikael Djurfeldt

SI-möte #10, Programkonstruktion och Datastrukturer

TDIU01 (725G67) - Programmering i C++, grundkurs

Strängar. Strängar (forts.)

Sammanfattning. Listor. List-manipulering. Matris. /home/lindahlm/activity-phd/teaching/11dd1310/exercise3/exercise3.py September 13, 20111

Introduktion till programmering SMD180. Föreläsning 2: Variabler, uttryck och satser

C++ - En introduktion

Programkonstruktion och datastrukturer. Moment 9 Om högre ordningens funktioner. PKD 2010/11 moment 9 Sida 1 Uppdaterad

Övning från förra gången: readword

Pascal... Pascal. Pascal... Pascal...

Lite mer om Javas stöd för fält. Programmering. Exempel: vad är det största talet? hh.se/db2004. Fält samt Input/Output

Pascal. reserverade ord fördefinierade funktioner och procedurer egendefinierade funktioner, procedurer och objekt

LÖSNINGSFÖRSLAG TENTAMEN PROGRAMMERING I ETT FUNKTIONELLT SPRÅK ML, 5P

Omtentamen (del 1, 6 högskolepoäng) i Programkonstruktion och datastrukturer (1DL201)

Kompilering och exekvering. Föreläsning 1 Objektorienterad programmering DD1332. En kompilerbar och körbar java-kod. Kompilering och exekvering

Programmering i C++ EDA623 Strömmar och filer. EDA623 (Föreläsning 9) HT / 19

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

Programstruktur och terminologi. Programmet producerar följande utskrift i terminalfönstret: Ett Javaprogram består av en eller flera klasser

Programkonstruktion och datastrukturer. Moment 9 Om högre ordningens funktioner. PKD 2011/12 moment 9 Sida 1 Uppdaterad

Former av rekursion. Programkonstruktion. Moment 5 Mera om rekursion. Fakultetsfunktionen. Största gemensamma delare (repris!

Objektorienterad Programmering (TDDC77)

I Skapa Hej.java och skriv programmet. I Kompilera med javac Hej.java. I Rätta fel och repetera tills du lyckas kompilera ditt program

Föreläsning 2 Programmeringsteknik och C DD1316

Filer. DA2001 (Föreläsning 16) Datalogi 1 Hösten / 19

TDP002 - Imperativ programmering

Att skriva till och läsa från terminalfönstret

Introduktion till formella metoder Programmeringsmetodik 1. Inledning

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 9 Om högre ordningens funktioner. PK1&PM1 HT-06 moment 9 Sida 1 Uppdaterad

729G04 Programmering och diskret matematik. Föreläsning 7

Programmering med Java. Grunderna. Programspråket Java. Programmering med Java. Källkodsexempel. Java API-exempel In- och utmatning.

Objektorienterad Programmering (TDDC77)

Hej Då, Karel! Programmering. Vårt första Javaprogram. hh.se/db2004. Java. Grundtyper, variabler och arrayer

Att deklarera och att använda variabler. Föreläsning 10. Synlighetsregler (2) Synlighetsregler (1)

Övning2. Variabler. Data typer

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Muddy. Funktioner / metoder. Punktnotation. Evalueringsordning

Programspråk. Programkonstruktion. Moment 1 Introduktion till programmering och programspråket Standard ML. Användning av funktionell programmering

Objektorienterad programmering i Java I. Uppgifter: 2 Beräknad tid: 5-8 timmar (OBS! Endast ett labbtillfälle) Att läsa: kapitel 5 6

Programmering i C++ En manual för kursen Datavetenskaplig introduktionskurs 5p

5 Grundläggande in- och utmatning

Datatyper och kontrollstrukturer. Skansholm: Kapitel 2) De åtta primitiva typerna. Typ Innehåll Defaultvärde Storlek

F4. programmeringsteknik och Matlab

TDIU01 - Programmering i C++, grundkurs

Tentamen. Datalogi I, grundkurs med Java 10p, 2D4112, Lördagen den 30 november 2002 kl , salar E33, E34

Deklarationer/definitioner/specifikationer

TDIU01 - Programmering i C++, grundkurs

Typsystem. Typsystem... Typsystem... Typsystem... 2 *

TDDC74 Programmering: Abstraktion och modellering Datortenta , kl 14-18

kind spelling Scanning

Typsystem. DA2001 (Föreläsning 23) Datalogi 1 Hösten / 19

Ordlistor, filhantering och ut på webben. Linda Mannila

Föreläsning 2 Programmeringsteknik och C DD1316. Programmering. Programspråk

Användarhandledning Version 1.2

Datalogi för E Övning 3

732G Linköpings universitet 732G11. Johan Jernlås. Översikt. Repetition. Felsökning. Datatyper. Referenstyper. Metoder / funktioner

Föreläsning 2 Programmeringsteknik och Matlab DD1312. Programspråk. Utskrift på skärmen

Uttryck, satser och strömhantering

Backcode. Jonathan Crusoe TDP019 Projekt: Datorspråk Linköpings universitet

Chapter 3: Using Classes and Objects

Föreläsning 10 Datalogi 1 DA2001. Utskrift på skärmen. Syntax. print( Hej ) Hur är det? Hej. print( Hej,end= ) print( Hur är det? ) HejHur är det?

729G04 Programmering och diskret matematik. Python 2: Villkorssatser, sanningsvärden och logiska operatorer

Programmering A. Johan Eliasson

Programmering för språkteknologer I, VT2012. Rum

Programmering II (ID1019) :00-11:00

Föreläsning 4: Filer och strömmar

Introduktion till programmering SMD180. Föreläsning 4: Villkor och rekursion

Alla datorprogram har en sak gemensam; alla processerar indata för att producera något slags resultat, utdata.

Tabeller. Programkonstruktion. Moment 8 Om abstrakta datatyper och binära sökträd. Implementering av tabellen. Operationer på tabellen

Objektorientering i liten skala

Datatyper. Programmering. Att definiera datatyper i Java. Laddade partiklar. (x,y) (Rx,Ry) hh.se/db2004

Pythons standardbibliotek

I ett program hantera man ofta samlingar av objekt av samma typ.

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 8 Om abstrakta datatyper och binära sökträd

Innehållsförteckning. Exempel. Åtkomst & användarhandledning

1/15/2013. DD1310/DD1314/DA3009 Programmeringsteknik. Lärandemål... Vilka läser kursen? ...fler lärandemål VARFÖR? Föreläsning 1

Visual Basic, en snabbgenomgång

Lite om reella tal. Programmering. I java. Om operatorers associativitet och prioritet

Laboration: Whitebox- och blackboxtesting

729G04 Programmering och diskret matematik

DD1314 Programmeringsteknik

F6: Högre ordningens funktioner. Mönster för rekursion (1) Mönster för rekursion (1b) Mönster för rekursion (2) Högre Ordningens Funktioner

TDDC77 Objektorienterad Programmering

Föreläsning 3: Typomvandling, villkor och val, samt textsträngar

Vem är vem på kursen. Objektorienterad programvaruutveckling GU (DIT011) Kursbok Cay Horstmann: Big Java 3rd edition.

Obligatorisk uppgift 5

Föreläsningsanteckningar, Introduktion till datavetenskap HT S4 Datastrukturer. Tobias Wrigstad

Datorlära 6. Arbeta med strängar Inmatning med tangentbordet Bygga ett program med inmatning, funktioner, osv

TDIU01 - Programmering i C++, grundkurs

Generiska konstruktioner. Kursbokens kapitel 13

Transkript:

"Referentiell transparens" Hittills har jag beskrivit körningen av ML-program genom att uttryck ersätts med sina värden: fun fact(0) = 1 fact(n) = n*fact(n-1) fact(3) ~> 3*fact(3-1) ~> 3*fact(2) ~> 3*(2*fact(2-1)) ~> 3*(2*fact(1)) ~> 3*(2*(1*fact(1-1))) ~> 3*(2*(1*fact(0))) ~> 3*(2*(1*1)) ~> 3*(2*1) ~> 3*2 ~> 6 Det enda "effekt" evalueringen av ett uttryck har är alltså att beräkna dess värde. (Jag bortser här från resursförbrukning tid, minne...). Man kan alltså fritt ersätta ett uttryck med ett annat uttryck som har samma värde, t.ex. kan f(x)*g(y) och g(y)*f(x) fritt bytas mot varandra i ett ML-program eftersom multiplikation är kommutativ. Det spelar ingen roll hur funktionerna f och g ser ut. Evalueringsordningen spelar alltså heller ingen roll (observera dock att man alltid anropar en funktion med fullständigt evaluerade uttryck ivrig evaluering). Detta gör ML-program lättare att förstå och ger också möjlighet för ML-systemet att "förbättra" programmet. f(x)^f(x) kan t.ex. ersättas av val fx = f(x) in fx^fx end...vilket lönar sig resursmässigt om beräkningen av f(x) tar lång tid eller har ett stort objekt som värde. Att man kan byta "lika mot lika" kallas referentiell transparens och är det normala hos funktionella språk.

Inmatning: problem Hittills har vi anropad ML-funktioner med hjälp av MLsystemet. ML-systemet har hanterat inläsning av indata och utskrift av resultat till/från ett terminalfönster. I praktiken räcker inte det: man vill kunna styra inläsning och utskrift själv och läsa/skriva även på filer. Låt oss anta att det finns en funktion readline: readline x : unit -> string SIDE-EFFECTS: En rad läses in från terminalfönstret POST: en sträng som innehåller den rad som lästs in. (x ignoreras) EXAMPLE: readline() = "Hej\n" om man skriver in raden "Hej\n" på tangentbordet. Vad blir readline(x)^readline(x)? Låt oss anta att användaren först skriver raden "Hej", sedan "hopp". (Varför slutar strängarna med \n?) readline()^readline() ~>"Hej\n"^readline() ~>"Hej\n"^"hopp\n" ~>"Hej\nhopp\n" Observera att readline() readline()!! Det beror på att readline inte bara lämnar tillbaka ett värde utan också läser en rad från terminalen. En ny rad läses en gång vid varje anrop. Antalet anrop blir väsentligt. let val r = readline() in r^r end ~> val r = "Hej\n" in r^r end ~> "Hej\n"^"Hej\n" ~> "Hej\nHej\n" Anropet av readline gör annat än bara beräknar värdet. Värdet bestäms av faktorer som kan ändras från anrop till anrop. readline har en sidoeffekt.

Sekvensering x ; y : a* b-> b POST: Värdet av y infix 0 ; Uttrycket x;y har som värde värdet av uttrycket y. Utan sidoeffekter vore ; onödig eftersom x;y = y, så alla förekomster av x;y kunde bytas ut mot y. Syftet med att skriva x;y är just möjligheten att det annars meningslösa uttrycket till vänster har en sidoeffekt. (Egentligen är ; ingen operator utan ett "nyckelord", men det fungerar ungefär som en operator.) Eftersom förekomster av sidoeffekter gör att lika inte kan bytas mot lika blir evalueringsordningen viktig. I ML beräknas uttryck från vänster till höger. Man kan alltså vara säker på att i x;y så beräknas x före y. Grupperingar med parentes ändrar inte detta det vänstraste hela deluttrycket beräknas först. fun square(x) = x*x square(2*3)*(4+5) ~> square(6)*(4+5) ~> (6*6)*(4+5) ~> 36*(4+5) ~> 36*9 ~> 324 Exekveringsordningen är viktig att hålla reda på när man programmerar med sidoeffekter.

Utmatning: samma problem Funktioner i ML för utmatning ger också problem. Låt oss använda funktionen print: print x : string -> () SIDE-EFFECTS: Strängen x skrivs ut på terminalfönstret. POST: () EXAMPLE: print("hej\n") = () och skriver samtidigt raden "Hej\n" på terminalfönstret. Vad blir print("hej\n");print("hej\n")? print("hej\n");print("hej\n") ~> ();print("hej\n") ~> ();() ~> () Samtidigt skrivs raden "Hej\n" ut två gånger. Observera att print("hej\n") = print("hej\n"), men det hjälper inte! Det beror på att print inte bara lämnar tillbaka ett värde utan också skriver en rad på terminalen. En ny rad skrivs en gång vid varje anrop. Antalet anrop blir väsentligt. let val p = print("hej\n") in p;p end ~> val p = () in p;p end ~> ();() ~> () Samtidigt skrivs raden "Hej\n" ut en gång. Även print har sidoeffekt!

In- och utmatning i ML TextIO Det går att utföra in/utmating i ett funktionellt språk på ett sätt som i stort sett helt undviker sidoeffekter speciellt om språket har lat evaluering. Så har man tyvärr inte gjort i ML, utan all I/O i ML är baserad på sidoeffekter. För I/O finns olika bibliotek i ML, bl.a. biblioteket TextIO för in- och utmatning 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 inte känner igen annars. (Detta fungerar med alla bibliotek, även t.ex. String och List.) Man kan använda flera open-deklarationer. Jag förutsätter i fortsättningen att man använt deklarationen open TextIO. (Obs! open är ingen ersättning för load.)

In- och utmatning i ML strömmar Vid in/utmatning krävs en omfattande administration, bl.a. skall filer lokaliseras och man behöver hålla reda på hur långt i den aktuella filen man har läst/skrivit. Läspekare frithiof.txt: Der växte uti Hildings 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. För detta ändamål har ML i TextIO två abstrakta datatyper instream och outstream kallade strömmar. All information rörande adminstrationen av in/utmatning finns i strömmarna. För in/utmatning till terminalfönster finns i TextIO två fördefinierade strömmar stdin av typ instream och stdout av typ outstream. openin(s) : string -> instream PRE: s skall vara namnet på en fil som finns. POST: En instream kopplad till filen s. EXAMPLE : openin("frithiof.txt") SIDE-EFFECTS: Olika anrop till openin ger olika strömmar. openout(s) : string -> 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 inte finns. Olika anrop till openout ger olika strömmar.

Inmatning ett urval funktioner inputline(is) : instream -> string POST: Nästa rad från strömmen is avslutad med #"\n", eller tom sträng om inget kunde läsas (t.ex. EOF) SIDE-EFFECTS: Läspekaren för is flyttas fram. input1(is) : instream -> char option POST: SOME c om nästa tecken i strömmen är c, NONE om inget tecken kunde läsas (t.ex. EOF). SIDE-EFFECTS: Läspekaren för is flyttas fram. inputall(is) : instream -> string POST: Resterande text i strömmen is till filens slut. SIDE-EFFECTS: Läspekaren för is flyttas fram. endofstream(is) : instream -> bool POST: true om läsningen av strömmen is har nått till slutet av filen, false annars. SIDE-EFFECTS: inga. closein(is) : instream -> unit POST: (). SIDE-EFFECTS: Avslutar användningen av strömmen is. Exempelfunktionen readline kan definieras så här: fun readline() = inputline(stdin);

Inmatning: exempel Om vi har en fil testin.txt, med innehåll: Detta är första raden......och detta är andra. En rad mitt i......och sista Så kan vi göra följande: - val x = openin("testin.txt"); > val x = <instream> : instream - inputline(x); > val it = "Detta är första raden...\n" : string - input1(x); > val it = SOME #"." : char option - input1(x); > val it = SOME #"." : char option - inputline(x); > val it = ".och detta är andra.\n" : string - endofstream(x); > val it = false : bool - inputall(x); > val it = "En rad mitt i...\n...och sista\n" : string - endofstream(x); > val it = true : bool - input1(x); > val it = NONE : char option - inputline(x); > val it = "" : string - closein(x); -

Utmatning ett urval funktioner output(os,s) : outstream*string -> unit POST: () SIDE-EFFECTS: Strängen s skrivs ut på strömmen os. output1(os,c) : outstream*char -> unit POST: () SIDE-EFFECTS: Tecknet c skrivs ut på strömmen os. flushout(os) : outstream -> () POST: (). SIDE-EFFECTS: Skriver ut bufferten för strömmen os. closeout(os) : outstream -> unit POST: (). SIDE-EFFECTS: Avslutar användningen av strömmen os. Exempelfunktionen print finns fördefinierad så här: fun print(s) = (output(stdout,s); flushout(stdout);

Utmatning: exempel Så kan vi göra följande: - val x = openout("testut.txt"); > val x = <outstream> : outstream - output(x,"hej"); - output1(x,#","); - output(x," Hopp\n"); - output(x,"i det gröna\n"); - closeout(x); - Nu finns en fil testut.txt, med innehåll: Hej, Hopp I det gröna

In/utmatning mot terminalfönster Exempel på in/utmatning mot terminalen. Inmatad text är satt med fetstil. Se upp med buffring av in- och utdata!: - inputline(stdin); abcd > val it = "abcd\n" : string - output(stdout,"den röda räven rev en annan räv\n"); Den röda räven rev en annan räv - output(stdout,"här saknas radslut:"); Här saknas radslut: - (output(stdout,"här buffras utdata!\n"); inputline(stdin)); Nu skriver jag in en rad Här buffras utdata! > val it = "Nu skriver jag in en rad\n" : string - (output(stdout,"här töms bufferten först\n"); flushout(stdout); inputline(stdin)); Här töms bufferten först Nu skriver jag in en ny rad > val it = "Nu skriver jag in en ny rad\n" : string - (print("det går lika bra med print\n"); inputline(stdin)); Det går lika bra med print Nu skriver jag in en 3:e rad > val it = "Nu skriver jag in en 3:e rad\n" : string - input1(stdin); Nu skriver jag en rad igen > val it = SOME #"N" : char option - 2+2;! Toplevel input:! u skriver jag en rad igen! ^! Unbound value identifier: u

Tillägg till befintliga filer openout skapar en ny fil eller skriver över en befintlig fil. Man kan också vilja lägga till ny information i slutet av en fil. openappend(s) : string -> 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 inte finns. Skrivningen börjar i slutet av filen. Olika anrop till openappend ger olika strömmar. Om vi har en fil testut.txt, med innehå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 innehålla: Hej, Hopp I det gröna sade Frithiof

In- och utmatning av annat än text Ofta vill man läsa och skriva annan information än text t.ex. tal. I så fall måste man översätta mellan talen och en strängrepresentation av dem. (* inttostring(i) : int -> string POST: En textrepresentation av talet i. SIDE-EFFECTS: inga. fun inttostring(i) = let (* itsaux(i) : int -> string PRE: i > 0 POST: En textrepresentation av i. SIDE-EFFECTS: inga. INDUCTION VARIABLE: i WELL-FOUNDED RELATION: < *) fun itsaux(0) = "" itsaux(n) = itsaux(n div 10) ^ Char.toString(chr(ord(#"0")+ (n mod 10))) in if i = 0 then "0" else if i < 0 then "~" ^ itsaux(~i) else itsaux(i) end - print(inttostring(2) ^ " gånger " ^ inttostring(3) ^ " är " ^ inttostring(2*3) ^ "\n"); 2 gånger 3 är 6

Inmatning av tal är svårare Försöker man konvertera en sträng till ett tal så kan det hända att strängen inte representerar ett korrekt tal, t.ex. "a12b". (* intfromstring(s) : string -> int option POST: SOME i om strängen är en representation av talet i, annars NONE SIDE-EFFECTS: inga. *) fun intfromstring(s) = let (* ifsaux(n, acc): int*int -> int option PRE: n >= 0 och n <= size(s) POST: SOME i+acc*(10**(size(s)-n)), om s från pos. n till slutet representerar talet i, annars NONE SIDE-EFFECTS: inga. INDUCTION-VARIABLE: n WELL-FOUNDED ORDERING: < på size(s)-n *) fun ifsaux(n, acc) = if n = size(s) then SOME acc else let val digit = ord(string.sub(s,n))-ord(#"0") in if digit < 0 orelse digit > 9 then in NONE else ifsaux(n+1, acc*10+digit) end

Inmatning av tal är svårare (forts.) if size(s) > 0 andalso String.sub(s,0) = #"~" then if size(s) = 1 then NONE else case ifsaux(1, 0) of NONE => NONE SOME i => SOME (~i) else if size(s) = 0 then NONE else ifsaux(0, 0) end intfromstring("~12") ~> if size("~12") > 0 andalso String.sub("~12",0) = #"~" then... else... ~> if 2 > 0 andalso #"~" = #"~" then... else... ~> if size("~12") = 1 then NONE else case... ~> if 3 = 1 then NONE else case... ~> case ifsaux(1, 0) of NONE => NONE SOME i => SOME (~i)... ~> case SOME 12 of NONE => NONE SOME i => SOME (~i) ~> SOME (~12)

Hur fungerar ifsaux? fun ifsaux(n, acc) = if n = size(s) then SOME acc else let val digit = ord(string.sub(s,n))-ord(#"0") in if digit < 0 orelse digit > 9 then NONE else ifsaux(n+1, acc*10+digit) end (s = "~12") ifsaux(1,0) ~> if 1 = size("~12") then SOME 0 else let.. ~> let val digit = ord(string.sub("~12",1))- ord(#"0") in... end ~> let val digit = ord(#"1")-ord(#"0") in... end ~> let val digit = 49-48 in... end ~> let val digit = 1 in... end ~> if 1 < 0 orelse 1 > 9 then NONE else ifsaux(1+1, 0*10+1) ~> ifsaux(2,1) ~> if 2 = size("~12") then SOME 1 else let.. ~> let val digit = ord(string.sub("~12",2))- ord(#"0") in... end ~> let val digit = ord(#"2")-ord(#"0") in... end ~> let val digit = 50-48 in... end ~> let val digit = 2 in... end ~> if 2 < 0 orelse 2 > 9 then NONE else ifsaux(2+1, 1*10+2) ~> ifsaux(3,12) ~> if 3=size("~12") then SOME 12 else let.. ~> SOME 12

Mer om konvertering ML innehåller en mängd biblioteksrutiner för konvertering, se kursboken appendix D.9. Vårt exempel inttostring är samma som biblioteksfunktionen Int.toString. Vårt exempel intfromstring liknar biblioteksfunktionen Int.fromString. I verkligheten är konvertering från strängar besvärligt eftersom man i allmänhet vill konvertera en sträng med olika typer av information blandade efter varandra, t.ex: "beräkna 1+23*4 slut". Ur denna sträng skulle man vilja få: strängen "beräkna" talet 1 tecknet + talet 23 tecknet * talet 4 strängen "slut" Dessutom vill man kanske ha 1+23*4 representerat som ett träd. Detta problem kallas parsning.

En kalkylator i ML fun calc() = let fun calcread() = (print("skriv ett tal: "); let val t = inputline(stdin) in case intfromstring( String.substring(t,0,size(t)-1)) of NONE => (print("felaktigt tal\n"); calcread()) SOME i => i end) fun calcop(x) = (print "Skriv en operation: "; case inputline(stdin) of "q\n" => () "c\n" => calcop(calcread()) "+\n" =>calcprint(x+calcread()) "-\n" =>calcprint(x-calcread()) "*\n" =>calcprint(x*calcread()) "/\n" => calcprint(x div calcread()) _ => (print("felaktig operation\n"); calcop(x))) and calcprint(x) = (print("resultatet är: "); print(inttostring(x)^"\n"); calcop(x)) in calcop(calcread()) end

Körning av kalkylatorn - calc(); Skriv ett tal: 12 Skriv en operation: - Skriv ett tal: 2 Resultatet är: 10 Skriv en operation: / Skriv ett tal: 2 Resultatet är: 5 Skriv en operation: * Skriv ett tal: 2 Resultatet är: 10 Skriv en operation: + Skriv ett tal: 2 Resultatet är: 12 Skriv en operation: c Skriv ett tal: 1 Skriv en operation: + Skriv ett tal: 2 Resultatet är: 3 Skriv en operation: q