Programkonstruktion och datastrukturer. Moment 2 Funktioner och programutveckling. PKD 2011/12 moment 2 Sida 1 Uppdaterad

Relevanta dokument
Programkonstruktion och datastrukturer. Moment 2 Funktioner och programutveckling. PKD 2012/13 moment 2 Sida 1 Uppdaterad

Återanvändning av kod? Programkonstruktion. Moment 2 Introduktion till funktioner och programutveckling. Funktionsdefinitioner. Egna funktioner i ML

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 2 Introduktion till funktioner och programutveckling

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

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

Programmeringsmetodik DV1 Programkonstruktion 1. Moment 4 Om rekursion. PK1&PM1 HT-06 moment 4 Sida 1 Uppdaterad

Summera godtyckligt antal tal. Programkonstruktion. Moment 4 Om rekursion. Fullständigt resonemang för summeringen. Analys av summeringsproblemet

Programkonstruktion. Tentamen,

Programkonstruktion. Tentamen,

Introduktion till formella metoder Programmeringsmetodik 1. Inledning

C++ Funktioner 1. int summa( int a, int b) //funktionshuvud { return a+b; //funktionskropp } Värmdö Gymnasium Programmering B ++ Datainstitutionen

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

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

Programmeringsteknik med C och Matlab

Programmering A. Johan Eliasson

1 Funktioner och procedurell abstraktion

Användarhandledning Version 1.2

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

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

Programmeringsmetodik DV1, Programkonstruktion del 1 Tentamen,

Programkonstruktion. Tentamen,

Programmera i C Varför programmera i C när det finns språk som Simula och Pascal??

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

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

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

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

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

Objektorienterad Programmering (TDDC77)

AssiML. Användarbeskrivning

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

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

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

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

TDP002 - Imperativ programmering

TDIU01 - Programmering i C++, grundkurs

Några inbyggda funktioner (med resultat!) Introduktion till programmering D0009E. Föreläsning 4: Villkor och rekursion. Modulus-operatorn.

Introduktion till programmering D0009E. Föreläsning 5: Fruktbara funktioner

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

Grundläggande programmering med C# 7,5 högskolepoäng

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

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

Omgivningar. Omgivningar är viktiga eftersom de avgör vilka namn som är synliga och därmed dessas innebörd och de värden som är förknippade med dem.

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

Föreläsning 3-4 Innehåll. Diskutera. Metod. Programexempel med metod

DD1314 Programmeringsteknik

Föreläsning 3-4 Innehåll

Introduktion till programmering SMD180. Föreläsning 5: Fruktbara funktioner

Tabeller. Programkonstruktion. Moment 8 Om abstrakta datatyper och binära sökträd. Specifikationer för tabellfunktionerna. Operationer på tabellen

Programkonstruktion och datastrukturer. Moment 4 Programmeringsmetodik och programvaruteknik. PKD 2011/12 moment 4 Sida 1 Uppdaterad

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

Visual Basic, en snabbgenomgång

6. Ge korta beskrivningar av följande begrepp a) texteditor b) kompilator c) länkare d) interpretator e) korskompilator f) formatterare ( pretty-print

Datalogi I, grundkurs med Java 10p, 2D4112, Fiktiv tentamen, svar och lösningar och extra kommentarer till vissa uppgifter 1a) Dividera förs

Språket Python - Del 1 Grundkurs i programmering med Python

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

Programkonstruktion och datastrukturer. Moment 4 Programmeringsmetodik och programvaruteknik. PKD 2010/11 moment 4 Sida 1 Uppdaterad

Klassdeklaration. Metoddeklaration. Parameteröverföring

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

TENTAMEN TDDB53. Programmering i Ada för MI (provkod TEN2) den 7 april 2010 kl Institutionen för datavetenskap, IDA Olle Willén mars 2010

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

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

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

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

Programmering, grundkurs, 8.0 hp HI1024, HI1900 etc., Tentamen TEN1. Måndagen den 10 januari 2011,

Tentamen i. TDDC67 Funktionell programmering och Lisp

DD1310/DD1314/DA3009 Programmeringsteknik LÄRANDEMÅL... Vilka läser kursen? ...FLER LÄRANDEMÅL. Föreläsning 1

Algebra och rationella uttryck

Tentamen i kurserna Beräkningsmodeller (TDA181/INN110) och Grundläggande Datalogi (TDA180)

Obligatorisk uppgift: Numerisk kalkylator

Programmera på riktigt

Grunderna i stegkodsprogrammering

Dagens föreläsning. Repetition. Repetition - Programmering i C. Repetition - Vad C består av. Repetition Ett första C-program

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

Programmering II (ID1019) :00-11:00

DD1361 Programmeringsparadigm. Carina Edlund

Objektorienterad Programmering (TDDC77)

Sammanfattningar Matematikboken Y

Regression med Genetiska Algoritmer

Objektorienterad programmering Föreläsning 8. Copyright Mahmud Al Hakim Agenda (halvdag)

Numeriska Metoder och Grundläggande Programmering för P1, VT2014

Labb i Datorsystemteknik och programvaruteknik Programmering av kalkylator i Visual Basic

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

Grundläggande datalogi - Övning 1

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

Dagens föreläsning Programmering i Lisp. - Bindning av variabler (avs 14.6) fria variabler statisk/lexikalisk och dynamisk bindning

Sidor i boken , , 3, 5, 7, 11,13,17 19, 23. Ett andragradspolynom Ett tiogradspolynom Ett tredjegradspolynom

Inlämningsuppgift MiniPlotter

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

Översikt över Visual Basic

Variabler och konstanter

Multipel tilldelning. Introduktion till programmering D0009E. Föreläsning 6: Iteration. while-satsen. Kom ihåg. Snurror kontra rekursion

TUTORIAL: KLASSER & OBJEKT

Introduktion till programmering SMD180. Föreläsning 9: Tupler

Föreläsning 2. Variabler, tilldelning och kodblock{} if-satsen Logiska operatorer Andra operatorer Att programmera

Extramaterial till Matematik Y

Laboration 1 Introduktion till Visual Basic 6.0

EDAA20 Programmering och databaser. Mål komprimerat se kursplanen för detaljer. Checklista. Föreläsning 1-2 Innehåll. Programmering.

Sätt att skriva ut binärträd

Objektorienterad programmering D2

Transkript:

Programkonstruktion och datastrukturer Moment 2 Funktioner och programutveckling PKD 2011/12 moment 2 Sida 1 Uppdaterad 2011-11-07

Återanvändning av kod? String.substring("Hägg",size "Hägg"-3,3) > "ägg" Detta uttryck plockar ut ett visst antal tecken (3) ifrån slutet av en teckensträng ("Hägg"). Vill man plocka ut ett annat antal tecken eller plocka ur en annan sträng så måste man skriva om uttrycket med delar utbytta: String.substring("Ångström",size "Ångström"-5,5) > "ström" Att behöva skriva om likadan kod tar tid och leder lätt till misstag (som att bara byta ut 3 mot 5 på ena stället). Man vill göra koden oberoende av vilken sträng och teckenantal det gäller. Hur lösa detta problem? PKD 2011/12 moment 2 Sida 2 Uppdaterad 2011-11-07

Egna funktioner i ML Man kan definiera egna funktioner (eg. funktionsprocedurer) som är oberoende av speciella indata kan användas flera gånger utan kopiering ges indata varje gång de används. > fun last(s,n) = # String.substring(s,size s-n,n); val last = fn : string * int -> string Denna deklaration definierar en ny funktion last som tar fram tecken från slutet av en sträng! s och n är namnen på indata till funktionen. > last("hägg",3); val it = "ägg" : string Ett ML-program byggs upp av (flera) sådana funktionsdefinitioner. PKD 2011/12 moment 2 Sida 3 Uppdaterad 2011-11-07

Jämför med dataflödesdiagram fun last(s,n) = String.substring(s,size s-n,n); size - String.sub string last PKD 2011/12 moment 2 Sida 4 Uppdaterad 2011-11-07

Beräkning av funktionsanrop Ett funktionsanrop beräknas som om man bytt ut indatanamnen mot de verkliga indata. fun last(s,n) = String.substring(s,size s-n,n); last("hägg",3) > String.substring("Hägg",size "Hägg"-3,3) > String.substring("Hägg",4-3,3) > String.substring("Hägg",1,3) > "ägg" last("ångström",5) > String.substring("Ångström", size "Ångström"-5,5) > String.substring("Ångström",8-5,5) > String.substring("Ångström",3,5) > "ström" PKD 2011/12 moment 2 Sida 5 Uppdaterad 2011-11-07

Funktionsdefinitioner funktionskropp funktionsnamn formellt argument fun add1 x = x+1 Namnet add1 binds till det nya funktionsuttrycket av deklarationen. När man anropar funktionen (applicerar den på argument) så binds x till argumentet i anropet (det aktuella argumentet) medan funktionskroppen beräknas. x kallas för en bunden variabel. Funktionskroppen beräknas som om x bytts ut mot det aktuella argumentet. Funktionen sägs returnera det beräknade värdet. Valet av x som identifierare är godtyckligt. add1(4*2) - > add1 8 - > 8+1 - > 9 Funktionens typ bestäms av typerna hos det formella argumentet och funktionskroppen. Funktionen ovan har typen int -> int. PKD 2011/12 moment 2 Sida 6 Uppdaterad 2011-11-07

fun add1 x = x+1; add1 3+add1(4*2) - > (3+1)+add1(4*2) - > 4+add1(4*2) - > 4+add1 8 - > 4+(8+1) - > 4+9 - > 13 add1(3*add1(4*2)) - > add1(3*add1 8) - > add1(3*(8+1)) - > add1(3*9) - > add1 27 - > 27+1 - > 28 Mer exempel... PKD 2011/12 moment 2 Sida 7 Uppdaterad 2011-11-07

Program på fil Normalt skapar man sitt ML-program i en texteditor (t.ex. Emacs) och sparar på fil där namnet slutar med.sml. example.sml fun last(s,n) = String.substring(s,size s-n,n); Filer läses in i ML med kommandot use. (Egentligen är use också en funktion detta återkommer jag till i slutet av kursen.) Filen behandlas (nästan) som om texten i filen skrivits in för hand. > use "example.sml"; val last = fn : string * int -> string val it = () : unit > last("hägg",3); val it = "ägg" : string PKD 2011/12 moment 2 Sida 8 Uppdaterad 2011-11-07

Inuti programfilen... Programfiler innehåller typiskt en mängd deklarationer, men även uttryck kan finnas där. Funktioner (och namngivna värden) måste definieras innan de används. Semikolon mellan deklarationer kan utelämnas om ML genom förekomsten av nyckelord kan avgöra var en deklaration eller slutar och nästa börjar. Kommentarer (*... *) läggs in i programfiler för att förklara programkoden där. Mellanslag och radbrytningar kan fritt läggas in mellan symboler för att göra programkoden mera läslig. PKD 2011/12 moment 2 Sida 9 Uppdaterad 2011-11-07

ML-mod i Emacs Man kan köra ML inifrån Emacs. Detta är praktiskt bl.a. kan man redigera och köra ML-program ifrån samma verktyg (Emacs). Kommandot M-x sml-poly-ml startar Poly/ML i en buffert med namnet *sml*. I bufferten *sml* skriver du uttryck, deklarationer etc. När du trycker på Return skickas texten på samma rad till ML. Har du ett ML-program i en annan buffert kan du skicka programmet till ML med kommandot C-c C-b när du är i denna buffert. Man kan bläddra bland tidigare indata till ML med M-p och M-n. Mera information finns på kurswebben (välj Programvara i menyn). PKD 2011/12 moment 2 Sida 10 Uppdaterad 2011-11-07

Avbryta ML Ett ML-program som kör kan avbrytas med ^C (C-c C-c i Emacs, eller om det inte fungerar C-q C-c Return). PKD 2011/12 moment 2 Sida 11 Uppdaterad 2011-11-07

Abstrakt och konkret Med abstraktion i programmering menas att man döljer eller skjuter åt sidan vissa egenskaper hos något. Detta gör man för att förenkla programmeringen så att man inte skall behöva tänka på detaljer. Du vill åka buss från Polacksbacken till Flogsta. Då behöver du inte veta vilken buss (alltså vilket fordon) du skall åka med vilket kan vara olika från gång till gång utan du åker med busslinje 21. Linje 21 är en abstraktion av de bussar som faktiskt kör mellan (bl.a.) Polacksbacken och Flogsta. PKD 2011/12 moment 2 Sida 12 Uppdaterad 2011-11-07

Abstrakt och konkret (forts.) I programmering abstraherar man data genom att dölja hur data representeras. strängen "Hej, hopp" representeras egentligen som en följd av teckenkoder, t.ex. (med ISO 8859-1 kod) 72, 101, 106, 44, 32, 104, 111, 112, 112 flyttalet 3.12 representeras egentligen som två tal, en mantissa och en exponent, t.ex. 312 och 2, där innebörden är 312*10-2 Hur data verkligen ser ut döljs av programspråket. Är mantissan och exponenten 312 resp. 2 eller är de 0.312 och 1? Det spelar ingen roll det viktiga är hur man kan arbeta med dem. (Den konkreta) representationen har abstraherats bort. PKD 2011/12 moment 2 Sida 13 Uppdaterad 2011-11-07

Definitioner Bindning kan ses som att namnet definieras att betyda ett värde. Detta är en form av abstraktion som kallas definitionsabstraktion. Man bortser från det konkreta värdet och använder istället ett abstrakt namn, i syfte att ge enkla/meningsfulla namn åt värden (t.ex. Math.pi i stället för 3.14159265 underlättar läsning.) visa på en speciell användning av ett uttryck (t.ex. dagarpervecka eller maxantal i stället för 7. Man kan lätt ändra värdet i en användning utan att riskera påverka andra.) undvika dubbla beräkningar (t.ex. val a = 5*5; a*a; beräknar 5*5*5*5 med bara två multiplikationer) Symbolerna som utgör namn kallas identifierare. PKD 2011/12 moment 2 Sida 14 Uppdaterad 2011-11-07

(Rosens) namn What's in a name? That which we call a rose by any other name would smell as sweet. W. Shakespeare, Romeo och Julia Namnet som sådant saknar i de flesta programspråk betydelse. Det är bara den mänskliga läsaren av ett program som kan tolka namnet. Därför är det viktigt att ett tolkningen av ett namn faktiskt stämmer överens med innebörden av det som namnet används till. Man kan byta ut alla namn inom ett program (om det görs på ett konsekvent sätt) utan att påverka programmets funktion alls. (Detta gäller förstås inte namn som är definierade utanför själva programmet t.ex. Math.pi som är definierat i språket ML.) PKD 2011/12 moment 2 Sida 15 Uppdaterad 2011-11-07

Att deklarera Funktionsabstraktion fun last(s,n) = String.substring(s,size s-n,n); kallas för en funktionsabstraktion. Man har abstraherat bort konkreta data (t.ex. strängen "Hägg" och antalet 3) för att få ett mer generellt abstrakt uttryck. Beräkningar med konkreta data har också blivit mer abstrakt uttrycket String.substring("Hägg",size "Hägg"-3,3) ersätts av ett uttryck last("hägg",3), där den exakta metoden att utföra beräkningen är bortabstraherad. Endast namnet last används definitionsabstraktion igen! PKD 2011/12 moment 2 Sida 16 Uppdaterad 2011-11-07

Funktionsabstraktion i verkliga livet Du vill skicka ett intyg till CSN-kontoret i Uppsala. En metod är att be en kompis ta papperet och gå till CSN med det. En annan är att stoppa det i ett kuvert och lägga i en brevlåda Postens brevförmedling utför samma sak som din kompis men är mer abstrakt eftersom du inte behöver veta hur det går till att få papperet till CSN. mer generell eftersom du kan använda posten för att skicka papper vart som helst (din kompis vill kanske inte åka till Sundsvall för att lämna ett papper till CSN huvudkontor). PKD 2011/12 moment 2 Sida 17 Uppdaterad 2011-11-07

Exempel: Kalkylator för rationella tal Önskemål: Ett program som skall ta emot två rationella tal (bråktal) och ett av de fyra räknesätten, utföra den angivna uträkningen och visa resultatet. Programmet skall heta qcalc. Innan vi kan skriva programmet måste vi konstruera datastrukturer och algoritmer för den beskrivna uppgiften. Huvudprincip: stegvis förfining. Om problemet inte kan överblickas, bryt ned det i delproblem och tillämpa samma princip på dessa. Överblickbara problem kan kodas direkt. PKD 2011/12 moment 2 Sida 18 Uppdaterad 2011-11-07

Rationella tal Definition: tal som kan skrivas som en kvot av två heltal (täljare och nämnare) där nämnaren 0. Mängden av rationella tal betecknas Q. Räkneregler: x1 y1 x1*y2+y1*x2 x1 y1 x1*y1 + = * = x2 y2 x2*y2 x2 y2 x2*y2 x1 y1 x1*y2 y1*x2 x1 y1 x1*y2 = / = x2 y2 x2*y2 x2 y2 x2*y1 PKD 2011/12 moment 2 Sida 19 Uppdaterad 2011-11-07

Tupler i ML Ett rationellt tal består av två heltal som hör ihop. Grupper av data kan hållas ihop i en tupel (post). Tupler är konstruerade och inte grundläggande datatyper. Syntax i ML: (data1,data2,...,datan) n är minst 2. Motsvarande typ: typ1*typ2*...*typn Ex: (1,6) : int*int Ex: (1.0,"Hej",true) : real*string*bool Åtkomst till komponenter (fält): #n (där n är ett heltal) ger fält n. Ex: #2 (1.0,"Hej",true) > "Hej" (ML kan också använda poster med namngivna fält.) OBS! # i sig är ingen funktion man kan inte skriva t.ex. #(n+1) för att få ut fält nummer n+1. PKD 2011/12 moment 2 Sida 20 Uppdaterad 2011-11-07

Datarepresentation Ett rationellt tal representeras naturligt som ett talpar (2-tupel) av heltal, int*int, där första talet är täljaren och det andra nämnaren. Alla 2-tupler av heltal representerar emellertid inte något rationellt tal det gör inga tupler där andra talet (nämnaren) är 0. Andra heltalet i varje par måste alltså vara skiljt från 0. Detta är en invariant ett villkor som måste bevaras av programmet. Räknesättet som skall utföras kan representeras av ett tecken, #"+", #"-", #"*" eller #"/". Även här krävs en invariant inget annat tecken får förekomma. Andra representationer är också tänkbara. PKD 2011/12 moment 2 Sida 21 Uppdaterad 2011-11-07

Problemlösning - stegvis förfining Kalkylator Beräkna täljare för addition Beräkna nämnare för addition Välj räknesätt: add.? I så fall, utför addition subtr.? I så fall, utför subtraktion mult.? I så fall, utför multiplikation div.? I så fall, utför division Beräkna täljare för subtraktion Beräkna nämnare för subtraktion Första förfiningssteget: skriv huvudfunktionen qcalc med typen (int*int)*(int*int)*char -> int*int PKD 2011/12 moment 2 Sida 22 Uppdaterad 2011-11-07

Minns ni algoritmerna? En algoritm är en beskrivning av en beräkning som är: 1) Mekanisk (skall inte kräva intelligens för att förstå) 2) Behöver ändliga resurser (måste bli färdig) 3) Deterministisk (det skall alltid vara entydigt vad som skall göras) 4) Aritmetiserad (problemet skall kunna representeras i termer av data ytterst i form av heltal) Ett program är (oftast) en algoritm, men algoritmer kan skrivas mer människonära just för att beskriva beräkningar. T.ex. med hjälp av Klartext Pseudokod Flödesscheman Dataflödesdiagram PKD 2011/12 moment 2 Sida 23 Uppdaterad 2011-11-07

Problemlösningen som algoritm En algoritm kan uttryckas i klartext. Det är viktigt att man uttrycker sig noga, speciellt att man är försiktig med syftningar och att man tydligt skiljer på varje steg. Algoritm för beräkning med rationella tal. Indata: två rationella tal x och y samt ett räknesätt. 1. Är räknesättet +? I så fall, addera x och y. 2. Är räknesättet -? I så fall, subtrahera y från x. 3. Är räknesättet *? I så fall, multiplicera x och y. 4. Är räknesättet /? I så fall, dividera x med y. Algoritm för addition av rationella tal: Indata: två rationella tal x1/x2 och y1/y2 1. Täljaren i summan är x1*y2+y1*x2, nämnaren är x2*y2. PKD 2011/12 moment 2 Sida 24 Uppdaterad 2011-11-07

Pseudokod Pseudokod liknar programkod, men eftersom en människa och inte en maskin skall tolka den så behöver den inte vara lika strikt som ett program. Uttryckssättet kan också anpassas efter behovet. algoritm calc(x,y,räknesätt) om räknesätt = + så utför addition(x,y) om räknesätt = - så utför subtraktion(x,y) om räknesätt = * så utför multiplikation(x,y) om räknesätt = / så utför division(x,y) slut calc PKD 2011/12 moment 2 Sida 25 Uppdaterad 2011-11-07

Flödesschema Ett flödesschema visar i vilken ordning delberäkningar skall göras och vilka val som finns. Start. räknesätt = + Ja. Addera x och y Stopp. Nej. räknesätt = Nej. etc... Ja. Subtrahera y från x Stopp. PKD 2011/12 moment 2 Sida 26 Uppdaterad 2011-11-07

Huvudfunktionen i kalkylatorprogrammet fun qcalc(x,y,opr) = if opr = #"+" then qadd(x,y) else if opr = #"-" then qsub(x,y) else if opr = #"*" then qmul(x,y) else qdiv(x,y); Tack vare invarianten för räknesättet behöver man inte testa för #"/" det är den enda återstående möjligheten. De ännu inte skrivna underprogrammen har kallats qadd etc. Här kodar vi qcalc först och underprogrammen sedan (top-down). Alternativt kan man koda underprogrammen först (bottom-up). PKD 2011/12 moment 2 Sida 27 Uppdaterad 2011-11-07

Funktioner för de fyra räknesätten fun qadd(x:int*int,y:int*int) = (#1 x * #2 y + #1 y * #2 x, #2 x * #2 y); fun qsub(x:int*int,y:int*int) = (#1 x * #2 y - #1 y * #2 x, #2 x * #2 y); fun qmul(x:int*int,y:int*int) = (#1 x * #1 y, #2 x * #2 y); fun qdiv(x:int*int,y:int*int) = (#1 x * #2 y, #2 x * #1 y); Dessa funktioner motsvarar nästa förfiningssteg. De har alla typen (int*int)*(int*int) -> int*int. x:int*int betyder att man talar om för ML att x skall ha typen int*int. Mer om varför och när detta behövs kommer senare. PKD 2011/12 moment 2 Sida 28 Uppdaterad 2011-11-07

Exempel på räkning med rationella tal qcalc((1,2),(1,3),#"+") > if #"+" = #"+" then qadd((1,2),(1,3)) else if #"+" = #"-" then qsub((1,2),(1,3)) else if #"+" = #"*" then qmul((1,2),(1,3)) else qdiv((1,2),(1,3)) > if true then qadd((1,2),(1,3)) else > qadd((1,2),(1,3)) > (#1 (1,2) * #2 (1,3) + #1 (1,3) * #2 (1,2), #2 (1,2) * #2 (1,3)) > (1 * #2 (1,3) + #1 (1,3) * #2 (1,2), #2 (1,2) * #2 (1,3)) > (1 * 3 + #1 (1,3) * #2 (1,2), #2 (1,2) * #2 (1,3)) > (3 + #1 (1,3) * #2 (1,2), #2 (1,2) * #2 (1,3)) > > (5,6) PKD 2011/12 moment 2 Sida 29 Uppdaterad 2011-11-07

Ett sammansatt exempel Man kan förstås också använda funktionerna qadd, qsub, qmul och qdiv direkt för att utföra beräkningar: Medelvärdet av 1/2 och 3/2: qdiv(qadd((1,2),(3,2)),(2,1)) - > qdiv((#1(1,2)* #2(3,2)+ #1(3,2)* #2(1,2), #2(1,2)* #2(3,2)), (2,1)) - > qdiv((8,4),(2,1)) - > (#1(8,4)* #2(2,1),#2(2,1)* #1(8,4)) - > (8,8)...vilket är det samma som 1/1. (håll ordning på parenteserna...) PKD 2011/12 moment 2 Sida 30 Uppdaterad 2011-11-07

Definitionsabstraktion igen... visa på en speciell användning av ett uttryck. Om vi vill ändra tecknet som anger multiplikation till #"." dels måste man ändra i programmet (funktionen qcalc). dels måste man ändra i alla platser som anropar qcalc! Om vi glömmer det ena...eller ändrar där det inte skall ändras, så slutar programmet fungera. Lösning: namnge tecknen som anger olika räknesätt: val addition = #"+" val subtraktion = #" " val multiplikation = #"*" val division = #"/" PKD 2011/12 moment 2 Sida 31 Uppdaterad 2011-11-07

Kalkylatorprogrammet med namngivna konstanter fun qcalc(x,y,opr) = if opr = addition then qadd(x,y) else if opr = subtraktion then qsub(x,y) else if opr = multiplikation then qmul(x,y) else qdiv(x,y); Anropsexempel: qcalc((1,2),(1,3),multiplikation) > (1,6) Bara ett ställe (där bindningen av namnet sker) behöver ändras för att byta (t.ex.) multiplikationstecknet till #".". Dessutom blir programkoden mer läsbar! PKD 2011/12 moment 2 Sida 32 Uppdaterad 2011-11-07

etc. Exempel på räkning med namngivna konstanter qcalc((1,2),(1,3),addition) > qcalc((1,2),(1,3),#"+") > if #"+" = addition then qadd((1,2),(1,3)) else if #"+" = subtraktion then qsub((1,2),(1,3)) else if #"+" = multiplikation then qmul((1,2),(1,3)) else qdiv((1,2),(1,3)) > if #"+" = #"+" then qadd((1,2),(1,3)) else if #"+" = #"-" then qsub((1,2),(1,3)) else if #"+" = #"*" then qmul((1,2),(1,3)) else qdiv((1,2),(1,3)) > if true then qadd((1,2),(1,3)) else > qadd((1,2),(1,3)) PKD 2011/12 moment 2 Sida 33 Uppdaterad 2011-11-07

Olika representation av samma data Normalt är två saker lika om deras delar är lika och tvärtom. qcalc((1,2),(1,3),addition) > (5,6) qcalc((2,3),(1,6),addition) > (15,18) Nu är 5/6 = 15/18, men 5 15 och 6 18! Två olika representationer kan representera samma konkreta data. Detta leder till svårigheter när data skall jämföras i program. Hur lösa detta problem? PKD 2011/12 moment 2 Sida 34 Uppdaterad 2011-11-07

Lösningar på likhetsproblemet En lösning: Förfina datarepresentationen så att den blir unik t.ex. genom att kräva att rationella tal är maximalt förkortade (och att nämnaren är > 0) en ny invariant. (Programmen måste ändras!) En annan lösning: Skriv speciella jämförelseprogram, t.ex. fun qequal(x:int*int,y:int*int) = #1 x * #2 y = #2 x * #1 y; qequal((5,6),(15,18)) > #1 (5,6) * #2 (15,18) = #2 (5,6) * #1 (15,18) > 5 * #2 (15,18) = #2 (5,6) * #1 (15,18) > 5 * 18 = #2 (5,6) * #1 (15,18) > 90 = #2 (5,6) * #1 (15,18) > 90 = 6 * #1 (15,18) > 90 = 6 * 15 > 90 = 90 > true PKD 2011/12 moment 2 Sida 35 Uppdaterad 2011-11-07

Gungor och karuseller... Lösningen med förfinad datastruktur kräver extra beräkningar (gcd och förkortning) vid räkning med rationella tal, men kräver ingenting extra vid jämförelser. Lösningen med speciella jämförelseprogram kräver inget extra vid räkning med rationella tal, men kräver extra beräkningar vid jämförelser. Extra beräkningar tar tid. Den bästa lösningen beror på proportionen mellan räkning och jämförelser. (Men den ursprungliga representationen är dålig även av andra skäl nämnaren växer hela tiden...overflow ) PKD 2011/12 moment 2 Sida 36 Uppdaterad 2011-11-07

Kodgranskning och testning Vid programmering är risken stor att man gör misstag. När programmet skrivits måste man hitta (eventuella) felaktigheter. Syntaxfel, typfel o.dyl. upptäcks när programmet läses av datorn. Fel på algoritmer (logiska fel) och beräkningsfel måste upptäckas på annat sätt. Man korrekturläser (granskar) koden och tänker igenom att den gör den man tänkt sig. Man provkör (testar) koden och kontrollerar att resultatet blir det man förväntat sig. Ju tidigare man hittar ett fel desto bättre! Bäst är om programspråket är utformat så att möjligheten att skriva fel är liten. PKD 2011/12 moment 2 Sida 37 Uppdaterad 2011-11-07

Testfall Man måste välja ut data att provköra programmet med (testfall). qadd: 2/3 + 1/4 = 11/12 qsub: 2/3 1/4 = 5/12 qmul: 2/3 * 1/4 = 1/6 (man måste ta hänsyn till att samma tal kan representeras olika...) qdiv: 2/3 / 1/4 = 8/3 qcalc: Ett fall för var och en av hjälpfunktionerna (t.ex. fallen ovan). Vilka och hur många testfall man behöver beror på hur programmet ser ut och vad skall göra. Mer om det senare PKD 2011/12 moment 2 Sida 38 Uppdaterad 2011-11-07

Felsökning Om testerna ger fel resultat så måste man felsöka programmet för att förstå varför. Hitta buggen. Kontrollera att svaret verkligen är felaktigt! Förvissa dig om att (ev.) hjälpfunktioner gör rätt (dessa bör vara färdigtestade och felsökta först). Granska den anropade funktionen igen med speciell tanke på de indata som användes i testfallet. Utför beräkninen steg för steg för att se var felaktiga värden uppstår. (Det förekommer felsökningshjälpmedel där datorn kan göra steg-för-stegberäkningar. Vi måste dock göra det för hand.) PKD 2011/12 moment 2 Sida 39 Uppdaterad 2011-11-07

Felaktigt program fun qadd(x:int*int,y:int*int) = (#1 x * #2 y + #1 y * #2 x, #2 x * #2 y); fun qsub(x:int*int,y:int*int) = (#1 x * #2 y - #1 y * #2 x, #2 x * #2 y); fun qmul(x:int*int,y:int*int) = (#1 x * #1 y, #2 x * #2 y); fun qdiv(x:int*int,y:int*int) = (#1 x * #2 y, #2 x * #1 y); fun qcalc(x,y,opr) = if opr = #"+" then qadd(x,y) else if opr = #"+" then qsub(x,y) else if opr = #"*" then qmul(x,y) else qdiv(x,y); PKD 2011/12 moment 2 Sida 40 Uppdaterad 2011-11-07

Provkörning > qadd((2,3),(1,4)); val it = (11,12) : int * int > qsub((2,3),(1,4)); val it = (5,12) : int * int > qmul((2,3),(1,4)); val it = (2,12) : int * int > qdiv((2,3),(1,4)); val it = (8,3) : int * int > qcalc((2,3),(1,4),#"+"); val it = (11,12) : int * int > qcalc((2,3),(1,4),#"-"); val it = (8,3) : int * int > qcalc((2,3),(1,4),#"*"); val it = (2,12) : int * int > qcalc((2,3),(1,4),#"/"); val it = (8,3) : int * int FEL!!!! PKD 2011/12 moment 2 Sida 41 Uppdaterad 2011-11-07

Felsökning av det felaktiga programmet Kontrollera att svaret verkligen är felaktigt! JA! (Det skulle t.ex. ha kunnat vara rätt rationella tal, men uttryckt med andra täljare och nämnare som 5/6 och 15/18.) Förvissa dig om att (ev.) hjälpfunktioner gör rätt (dessa bör vara färdigtestade och felsökta först). JA! Granska den anropade funktionen igen med speciell tanke på de indata som användes i testfallet. AHA! if-grenen som anropar qsub testar för ett plustecken istället för ett minustecken PKD 2011/12 moment 2 Sida 42 Uppdaterad 2011-11-07

Typhärledning I de flesta statiskt typade programspråk måste programmeraren ange typen för varje funktion, men ML kan (nästan alltid) bestämma typen hos en funktion automatiskt genom typhärledning. fun last(s,n) = String.substring(s,size s-n,n); String.substring har typen string*int*int -> string. Eftersom s är första argument till String.substring så måste s ha typen string. Eftersom n är sista argument till String.substring så måste n ha typen int. Därmed måste last ha argumenttypen string*int. Eftersom värdet av String.substring också är värdet av last så måste last ha värdetypen string. Alltså har last typen string*int -> string. PKD 2011/12 moment 2 Sida 43 Uppdaterad 2011-11-07

Typhärledning med dataflödesdiagram fun last(s,n) = String.substring(s,size s-n,n); string int size string intint int - string int int int String.sub string string str ing last Varje linje måste ha samma typ vid alla anslutningar! PKD 2011/12 moment 2 Sida 44 Uppdaterad 2011-11-07

Misslyckad typhärledning Om ett program är feltypat så misslyckas typhärledningen. > fun last(s,n) = String.substring(s,size s-n,s); Error-Type error in function application. Function: String.substring:string*int*int->string Argument: (s, size s - n, s) : string*int*string Reason: Can't unify int (*In Basis*) with string (*In Basis*) (Different type constructors) Found near String.substring (s, size s - n, s) Här har man av misstag gett s som sista argument till String.substring. String.substring har typen string*int*int -> string. s är 1:a argument till String.substring så typen är string. s är 3:e argument till String.substring så typen är int. s kan inte vara int och string samtidigt programmet är feltypat. PKD 2011/12 moment 2 Sida 45 Uppdaterad 2011-11-07

Misslyckad typhärledning med dataflödesdiagram fun last(s,n) = String.substring(s,size s-n,s); konflikt!????? int size string intint int - string int int int String.sub string string str ing last PKD 2011/12 moment 2 Sida 46 Uppdaterad 2011-11-07

Oväntat resultat av typhärledning (1) Vilken typ har funktionen sumsquares? fun sumsquares(x,y) = x*x+y*y; PKD 2011/12 moment 2 Sida 47 Uppdaterad 2011-11-07

Oväntat resultat av typhärledning (2) Vilken typ har funktionen sumsquares? fun sumsquares(x,y) = x*x+y*y; * och + är båda överlagrade och har de alternativa typningarna int*int->int och real*real->real. Om ML inte kan avgöra typen på aritmetiska operationer så väljer den int! sumsquares får alltså typen int*int->int. Var avsikten att använda sumsquares på flyttal så får man typfel. Lösning: ange uttrycklig typ på något deluttryck! eller fun sumsquares(x:real,y) = x*x+y*y; fun sumsquares(x,y) = (x*x+y*y):real; PKD 2011/12 moment 2 Sida 48 Uppdaterad 2011-11-07

Tupler kan ge problem med typhärledning. Varför var vi tvungna att deklarera argumenttyperna för qadd m.fl.? fun qadd(x:int*int,y:int*int) = (#1 x * #2 y + #1 y * #2 x, #2 x * #2 y); ML kan inte veta om tuplerna skall ha två, tre eller flera komponenter och kan därför inte bestämma deras typ. Typhärledning går inte att genomföra. Genom att på lämpligt ställe uttryckligen ange typen av indata till int*int försvinner problemet. PKD 2011/12 moment 2 Sida 49 Uppdaterad 2011-11-07

Mönstermatchning De flesta funktioner skrivs med kod för flera olika fall. Den grundläggande konstruktionen för falluppdelning är if-then-else. fun sign x = if x = 0 then 0 else if x < 0 then ~1 else 1; Falluppdelning som jämför argument med konstanter kan göras direkt i funktionsuttrycket klausuler fun sign 0 = 0 sign x = if x<0 then ~1 else 1 mönster När funktionen anropas går ML igenom klausulerna i tur och ordning och ser väljer den första klausul där argumenten passar. PKD 2011/12 moment 2 Sida 50 Uppdaterad 2011-11-07

Matchning av sammansatta datatyper Matchning kan också göras över formen på tupler. Identifierare binds till komponenterna i tuplerna. Tag t.ex. qdiv. Denna funktion kan skrivas fun qdiv((x1,x2),(y1,y2)) = (x1*y2,x2*y1) qdiv((1,2),(3,4)) - > (1*4,2*3) - > (4,6) x1, x2, y1 och y2 byts ut mot respektive 1, 2, 3 och 4. I praktiken använder men oftast (där det är möjligt) denna metod (snarare än #) för att komma åt komponenter i tupler eftersom det ger mycket mer lättläst kod. Dessutom slipper man oklarheten kring antalet element i tuplerna det blir helt klart att varje tupel innehåller 2 komponenter. PKD 2011/12 moment 2 Sida 51 Uppdaterad 2011-11-07

Funktioner med flera argument har bara ett Funktioner i ML har egentligen alltid ett argument. Om funktionen ser ut att ha flera argument, t.ex. fun last(s,n) = last("hägg", 3) > "ägg"...så konstrueras vid anropet egentligen en tupel av argumenten och skickas som ett argument till funktionen. Formen hos de formella argumenten i funktionsdefinitionen är egentligen bara mönstermatchning i funktionens enda riktiga argument. Funktionstypen string*int -> string i detta exempel visar också det argumentet är en string*int-tupel. Av praktiska skäl säger man ofta ändå att last har två argument. PKD 2011/12 moment 2 Sida 52 Uppdaterad 2011-11-07

Matchning både på form och konstanter En signum-funktion för rationella tal: fun qsign((0,_)) = 0 qsign((x1,x2))= if x1*x2<1 then ~1 else 1; _ (understrykningstecken) kan användas istället för identifierare när man inte är intreserad av argumentets värde. Anropet qsign((0,4)) qsign((~2,3)) matchar första klausulen andra klausulen De dubbla parenteserna gör ingen skillnad mot enkla parenteser, men används för att betona att funktionen är tänkt att ha ett argument som är en 2-tupel. PKD 2011/12 moment 2 Sida 53 Uppdaterad 2011-11-07

Allmän form hos funktionsdefinitioner fun namn mönster1 = uttryck1 namn mönster2 = uttryck2... namn mönstern = uttryckn; Varje mönster är en konstant, en identifierare, symbolen _ eller skelettet till en datastruktur (som tupler) där komponenterna i sin tur är mönster. Alla mönster måste ha samma typ (funktionens argumenttyp). Alla uttryck måste ha samma typ (funktionens värdetyp). PKD 2011/12 moment 2 Sida 54 Uppdaterad 2011-11-07

Se upp med matchning 1 Det är viktigt att klausulerna kommer i rätt ordning! fun sign x = if x<0 then ~1 else 1 sign 0 = 0 sign 0 >...?? PKD 2011/12 moment 2 Sida 55 Uppdaterad 2011-11-07

Se upp med matchning 2 När man använder matchning mot datatyper där värdena är i form av identifierare (t.ex. bool) kan små felskrivningar ge syntaktiskt korrekta program med helt annorlunda beteenden! fun boolname true = "Sant" boolname false = "Falskt"; boolname true > "Sant" boolname false > "Falskt" Låt oss göra en liten felskrivning: fun boolname truw = "Sant" boolname false = "Falskt"; boolname true > "Sant" boolname false > "Sant" Varför?? PKD 2011/12 moment 2 Sida 56 Uppdaterad 2011-11-07

Se upp med matchning 3 Vid ett funktionsanrop måste argumenten matcha någon klausul: fun nametobool "Sant" = true nametobool "Falskt" = false; nametobool "Sant" > true nametobool "Falskt" > false nametobool "Kanske" > exekveringsfel (Match) I detta fall får man en varning när funktionen definieras. > fun nametobool "Sant" = true nametobool "Falskt" = false; Warning-Matches are not exhaustive. Found near fun nametobool "sant" = true nametobool "falskt" = false val nametobool = fn: string -> bool PKD 2011/12 moment 2 Sida 57 Uppdaterad 2011-11-07

Catchall -klausuler För att undvika exekveringsfel pga att ingen klausul matchar kan man lägga in en sista klausul som säkert matchar alla argument: fun talnamn 1 = "Ett" talnamn 2 = "Två" talnamn 3 = "Tre" talnamn _ = "Många"; Eller t.o.m. fun talnamn 1 = "Ett" talnamn 2 = "Två" talnamn 3 = "Tre" talnamn x = if x <= 0 then "Lite" else "Många"; PKD 2011/12 moment 2 Sida 58 Uppdaterad 2011-11-07

Kalkylatorprogrammet med matchning fun qadd((x1,x2),(y1,y2)) = (x1*y2 + y1*x2, x2*y2); fun qsub((x1,x2),(y1,y2)) = (x1*y2 - y1*x2, x2*y2); fun qmul((x1,x2),(y1,y2)) = (x1*y1, x2*y2); fun qdiv((x1,x2),(y1,y2)) = (x1*y2, x2*y1); fun qcalc(x,y,#"+") = qadd(x,y) qcalc(x,y,#"-") = qsub(x,y) qcalc(x,y,#"*") = qmul(x,y) qcalc(x,y,_) = qdiv(x,y); Fördelar: Mycket mera läsligt. Entydiga tupeltyper typdeklarationer behövs inte. Nackdel: Namngivna värden kan inte användas. Varför? PKD 2011/12 moment 2 Sida 59 Uppdaterad 2011-11-07

Svar på några frågor fun boolname truw = "Sant" boolname false = "Falskt"; truw är inte ett värde utan en identifierare som vilken som helst. Den kommer alltså att matcha varje argument till funktionen även false och samtidigt bindas till argumentet. Alltså: boolname false > "Sant" Av samma skäl kan inte namngivna värden användas i matchning de skulle matcha alla argument och bindas om. val addition = #"+" val subtraktion = #" " fun qcalc(x,y,addition) = qadd(x,y) qcalc(x,y,subtraktion) = qsub(x,y) qcalc((1,2),(1,3),subtraktion) > > qadd((1,2),(1,3)) > (5,6) PKD 2011/12 moment 2 Sida 60 Uppdaterad 2011-11-07

Case-uttryck Matchning väljer olika fall beroende på argumenten till en funktion. Matchning kan också användas för att välja olika fall beroende på värdet av ett uttryck. fun dummultiplikation(x,y) = case x*y of 1 => "Ett" 2 => "Två" 3 => "Tre" 4 => "Fyra" _ => "Mycket"; dummultiplikation(2,2) > "Fyra" dummultiplikation(2,3) > "Mycket" Liksom i if-then-else så beräknas inte uttrycken i case-klausulerna förrän man vet vilket alternativ som valts. PKD 2011/12 moment 2 Sida 61 Uppdaterad 2011-11-07

Bindning i case-mönster fun pratsammultiplikation(x,y) = case x*y of 1 => "Ett" 2 => "Två" 3 => "Tre" 4 => "Fyra" z => Int.toString z ^ "."; pratsammultiplikation(2,3) > case 2*3 of 1 => "Ett" 2 => "Två" > case 6 of 1 => "Ett" 2 => "Två" > Int.toString 6 ^ "." > "6" ^ "." > "6." Identifierare i mönstren byts alltså ut i klausulerna mot uttrycket efter case på liknande sätt som bundna identifierare byts mot argumenten i funktionsanrop. PKD 2011/12 moment 2 Sida 62 Uppdaterad 2011-11-07

Bindningsräckvidd Vi har sagt att bundna variabler i funktionskroppen byts ut mot respektive aktuella argument när en funktion anropas. I verkligheten är det lite mer komplicerat. fun pratsammultiplikation2(x,y) = (case x*y of 1 => "Ett" 2 => "Två" Räckvidd för denna 3 => "Tre" bindning 4 => "Fyra" x => Int.toString x) ^ "."; Vid ett anrop pratsammultiplikation(2,3) byts inte x ut mot 2 i den sista case-klausulen. Bindningen av x i case-uttrycket skuggar den första bindningen. Räckvidden för bindningen av x i funktionsdefinitionen innehåller inte sista case-klausulen. PKD 2011/12 moment 2 Sida 63 Uppdaterad 2011-11-07

Vad händer om variabler binds igen? fun pratsammultiplikation2(x,y) = (case x*y of 1 => "Ett" 2 => "Två" 3 => "Tre" 4 => "Fyra" x => Int.toString x) ^ "."; pratsammultiplikation(2,3) > (case 2*3 of... x => Int.toString x)^"." > (case 6 of... x => Int.toString x)^"." > Int.toString 6 ^ "." > "6" ^ "." > "6." Den nya bindningen av x skuggar den gamla. Räckvidden av det första x omfattar inte Int.toString x. PKD 2011/12 moment 2 Sida 64 Uppdaterad 2011-11-07

Bindningsomgivningar Ett annat sätt att förklara bindningar som bättre stämmer med vad som faktiskt händer i datorn är med bindningsomgivningar. Man håller reda på bindningarna och byter namn mot värde först när variabeln skall beräknas. pratsammultiplikation(2,3) > (case x*y of...)^"." [x > 2, y > 3] > (case 2*y of...)^"." [x > 2, y > 3] > (case 2*3 of...)^"." [x > 2, y > 3] > (case 6 of...)^"." [x > 2, y > 3] > (Int.toString x [x > 6]) ^"." [x > 2, y > 3] > (Int.toString 6 [x > 6]) ^"." [x > 2, y > 3] > "6"^"." [x > 2, y > 3] > "6." Bindningsomgivningen [x > 2, y > 3] (inte en del av MLsyntaxen!) anger att x och y tillfälligt är bundna till heltalen 2 och 3. PKD 2011/12 moment 2 Sida 65 Uppdaterad 2011-11-07

Bindningsordningen är viktig Alla namn (inklusive funktioner) måste bindas innan de används. Har man skrivit funktionen add1 med definitionen fun add1 x = x+1; och add2 med definitionen fun add2 x = add1(add1 x); Så måste add2 definieras efter add1 annars klagar ML på att add1 är obunden. > fun add2 x = add1(add1 x); Error-Value or constructor (add1) has not been declared Found near add1 (add1 x) Error-Value or constructor (add1) has not been declared Found near add1 (add1 x) PKD 2011/12 moment 2 Sida 66 Uppdaterad 2011-11-07

Bindning är inte tilldelning En ombindning av ett namn skuggar ev. tidigare bindingar av namnet, men ändrar dem inte. > val x = 2; x binds. val x = 2 : int > fun addx n = n+x x är en fri variabel i addx. val addx = fn : int -> int > addx 0; val it = 2 : int > val x = 4; x binds om... val x = 4 : int > addx 0; val it = 2 : int men det påverkar inte addx! addx använder den definition av x som gällde när addx själv definierades! Skall ombindningen av x märkas vid anrop av addx måste även addx definieras om. PKD 2011/12 moment 2 Sida 67 Uppdaterad 2011-11-07

Bindning är inte tilldelning (forts.) Samma sak gäller ändring av funktionsdefinitioner. > fun add1 x = x+2;...slant på tangentbordet... val add1 = fn : int -> int > fun add2 x = add1(add1 x); val add2 = fn : int -> int > add2 3; val it = 7 : int...hoppsan... > fun add1 x = x+1; Rättad definition. val add1 = fn : int -> int > add1 3; val it = 4 : int Ok nu. > add2 3; val it = 7 : int Fortfarande fel! Även add2 måste definieras om när add1 ändras Lösning på detta problem: Ändra inte en funktion direkt i Poly/ML, utan lägg programmet i en fil och läs in hela filen efter ändring. PKD 2011/12 moment 2 Sida 68 Uppdaterad 2011-11-07

Funktionsvärda uttryck Funktioner i ML är första klassens objekt och kan beräknas med t.ex. if-then-else precis som andra värden. Alternativ qcalc: fun qcalc(x,y,opr) = (if opr = #"+" then qadd else if opr = #"-" then qsub else if opr = #"*" then qmul else qdiv) (x,y); qcalc((1,2),(1,3),#"+") > (if #"+"= #"+" then qadd else...)((1,2),(1,3)) > (if true then qadd else...) ((1,2),(1,3)) > qadd((1,2),(1,3)) > (5,6) PKD 2011/12 moment 2 Sida 69 Uppdaterad 2011-11-07

Anonyma funktioner Man kan skriva funktionsuttryck direkt där de skall användas utan separat definition en anonym funktion. Detta är ibland praktiskt. (fn x => x*x) 4 > 4*4 > 16 Vi utvidgar qcalc med medelvärdesoperation (a), men utan att definiera någon separat funktion för beräkningen: fun qcalc(x,y,opr) = (if opr = #"a" then fn (x,y) => qdiv(qadd(x,y),(2,1)) else if opr = #"+" then qadd ) (x,y); qcalc((1,2),(3,2),#"a") > (8,8) (Argumentnamnen till den anonyma funktionen, x och y, är samma som i huvudfunktionen. Vilken är räckvidden? Problem? Lämpligt?) PKD 2011/12 moment 2 Sida 70 Uppdaterad 2011-11-07

Funktionstyper är inte likhetstyper Man kan inte jämföra om två funktionsvärden med = eller <>! Anledningen är att det kan vara mycket svårt ja principiellt omöjligt att avgöra om två funktioner är lika. Är t.ex. (fn x => 2*x) = (fn x => if x<0 then x*2 else x+x) ja, det är det, men hur skall man kunna veta det? (Att man i allmänhet inte kan avgöra ifall två funktioner är lika är ett fundamentalt resultat som Turing visade.) Av liknande skäl kan man inte få ett funktionsvärde utskrivet ML svarar bara fn och med typen förstås. > qcalc; val it = fn : (int*int)*(int*int)*char->int*int PKD 2011/12 moment 2 Sida 71 Uppdaterad 2011-11-07

Syntaktiskt socker if B then E1 else E2 är exakt samma sak som case B of true => E1 false => E2 case E of P1=>E1 P2=>E2... Pn=>En är exakt samma sak som (fn P1=>E1 P2=>E2... Pn=>En) E fn P1=>E1 P2=>E2... Pn=>En är exakt samma sak som fn x => case x of P1=>E1 P2=>E2... Pn=>En Med syntaktiskt socker menas att man inför en form av uttryck som är ett enklare sätt att uttrycka något som redan finns i språket. if-then-else är onödig och case och funktionsmatchning utbytbara! PKD 2011/12 moment 2 Sida 72 Uppdaterad 2011-11-07