Go to: Solsystemet
title: Programmera med funktioner
author: Peter Ljung
Programmering med funktioner
Vad är en funktion?
Inom matematiken används funktioner som ett samband mellan två eller flera in och ut värden.
Exempelvis +
(addition) är en funktion
Skickar man in två värden 3
och 5
så skickar funktionen +
ut ett resultat 8
3
[ + ] 8
5
Andra vanliga funktioner inom matematiken är -
(subtraktion), *
(multiplikation) och /
(division)
3
[ - ] -2
5
9
[ * ] 27
3
Vad är värden och variabler?
Värden är en konstant tex. 3
. Inom matematiken använder man också variabeler för sätta ett namn på ett föränderligt värde.
En variabel kan representera ett godtyckligt värde x
, men kan också vara begränsat till ett värde tex. 3
.
x = 3
Variabler relaterar till varandra (begränsas) av de funktioner som sätter upp mellan dom tex.
Volymen V
av ett klot med radien r
beräknas genom funktionen:
V = (4/3) * pi * r * r * r
V
bestäms entydigt av r
enligt ekvationen ovan. Dvs. om r = 1
då är V = (3/4) * pi
Man kan också ha andra relationer än likhet tex. större än >
, lägre än <
eller skilt från =/=
Tex. om r < 1
då är också volymen av klotet V < (3/4) * pi
eller r =/= 1
då är också volymen av klotet V =/= (3/4) * pi
Typer av värden
Man delar in värden i olika kategorier av värden som definerar mängden av möjliga värden.
Exempel på värden är
- Hela tal: 1, 2, -1 ...
- Negativa tal: -12, -1 ...
- Jämna tal: 2, 4, 6, 102
- Decimala tal: 3.14, 2.4, -12.4
Hur många värden kan funktioner hantera?
Vi såg att +
är relationen mellan två (in) värden och ett (ut) värde
Men det finns funktioner som tar in och ger tillbaka fler eller färre värden
Vissa funktioner tar bara ett värde. Exempelvis + 1
som lägger till 1
till in värdet
5 [ + 1 ] 6
Andra tar hur många värden man vill. Exempelvis en funktion (length
) som anger hur många värden man matar in.
1, 4, 5, 3 "length" 4
3.14, 11.23 "length" 2
Hur många värden kan funktioner mata ut?
Funktioner kan även ge tillbaka flera värden. Exempelvis en funktion (reverse
) ordnar värdena i omvänd ordning
4, 76 "reverse" 76, 4
434, 33 "reverse" 33, 434
Konstanter kan man också se som funktioner utan invärde men konstant utvärde
pi = 3.14159 ...
- "pi" 3.14159 ...
Hur skriver man funktioner?
Matematiska funktioner skriver man normalt med de ingående värdena på var sida om funktionen (sk. infix notation)
3 + 5 = 8
9 * 3 = 27
Man kan också skriva funktionen först, med de ingående värdena efter funktionen (sk. prefix notation)
add 3 5
(+) 3 5
multiply 9 3
(*) 9 3
Ofta skriver man en funktion med värden som obestämda variabler
add a b
divide c d
Detta betyder att funktionen add
tar två okända värden (variabler) a
och b
Funktioner i programmering
Det visar sig att funktioner är mycket vanliga även inom programmering. Det som kan skilja sig är exakt hur man skriver funktioner i olika programmeringsspråk
Testa genom att öppna följade sida i en browser.
Här kan man testa att skriva funktioner i programmeringspråket, Haskell
Funktioner i Haskell
Testa exempelvis dessa kommandon i svarta rutan till höger på sidan. Tryck ENTER mellan varje kommando
3 + 5
3 - 5
9 / 3
pi / 2
(+1) 1
let add = (+)
(+) 3 5
add 3 5
3 `add` 5
length [1,4,5,3]
reverse [1,4,5,3]
Det är inte stor skillnad mellan hur man skriver matematiska funktioner och hur man skriver funktioner i Haskell. Haskell stödjer prefix notation och infix notation med några specialregler.
- Vanliga tecken funktioner som
+
,-
,*
och/
skrivs vanligtvis med infix notation och måste skrivas med paranteser för prefix notation - Namngivna funktioner som
pi
,add
,length
ochreverse
skrivs med vanligtvis med pre-fix notation och måste skrivas med "bak-tick" ````` för in-fix notation
Hur skapar man egna funktioner?
Om vi bestämmer värdena på in variablerna så har funktionen ett ut värde
add a b = 8 om a = 3 och b = 5
add a b = 10 om a = 5 och b = 5
Man kan själv skapa funktioner som kan räkna ut mer komplicerade saker
Exempelvis kan vi skriva funktionen for volymen av en kub med sidan s
såhär:
cube s = s*s*s
Skriv in funktionen och testa sedan den genom att använda funktionen med olika värden på s
cube 1
cube 3
I funktionen cube
återanvänder vi funktionen *
(multiplikation) för att räkna ut volymen av en kub.
Haskell har redan många fördefinerade funktioner tex. *
. Vår cube
utökade vi språket med.
Funktioner som anropar funktioner
Man kan skapa hur komplicerade funktioner som helst
Ofta använder en funktion andra funktioner som vi redan såg i det förra exemplet
I följande exempel skapar vi två nya funktioner cube
och sphere
som vi sedan använder i en tredje funktion sphere_in_cube
cube s = s*s*s
cube 3
sphere r = (4/3) * pi * r * r * r
sphere 3
sphere_in_cube r = cube r - sphere r
sphere_in_cube 3
Andra typer av värden
Funktioner behöver inte bara hantera siffror
Man kan representera vad som helst med en typ av data (datatyp). Exempelvis kan man tänka sig att man har en datatyp som representerar färger. Sedan kan man definera funktioner som jobbar med färger som in och ut data.
add yellow blue = green
Funktioner kan också jobba med bokstäver
Exempelvis en funktion (reverse
) som ger tillbaka texten baklänges
reverse "Programmering är funktioner!"
Man kan se grafiska former som värden
Exempelvis funktionen circle r
ge tillbaka en bild (Picture
) av en cirkel med radien r
circle r = Picture
Typer av värden
Olika värden kan delas upp i olika typer (eller klasser) av värden. Några exempel på typer är
- Heltal
- Decimaltal
- Färger
- Bilder
Vissa värden ingår i flera typer
Exempelvis heltal finns också bland decimaltal
13 är samma tal som 13.00000...
Funktioner kan ta och ge tillbaka olika typer av värden
Exempelvis funktionen round x
tar ett decimaltal och ger tillbaka ett heltal
round pi = 3
Testa olika typer
Testa några funktioner som tar olika typer in in och ut värde. Vilka typer är det?
round pi
pi - round pi
length "Hej"
Vad händer om du skickar in fel typ?
3 - "hej"
3 - length "hej"
pi - (round pi)
pi - fromIntegral (round pi)
Testa olika typer (2)
I det sista exemplet kan man ifrågasätta varför det inte går att subtrahera ett heltal 3
från pi
.
Programmeringsspråk kan ofta vara mer strikta vad gäller typer av värden än vad matematiken tillåter. Av den anledningen går det inte att subtrahera två tal av olika typ (decimaltal och heltal). För att komma runt detta måste man först konverera heltalet till ett decimaltal.
pi - (fromIntegral (round pi))
Funktionen fromIntegral
konverterar ett heltal till en lämplig numerisk typ i detta fall ett decimaltal. Man kan fråga vilken typ det är genom att skriva :t
framför uttrycket tex.
:t pi
pi :: Floating a => a
:t (round pi)
(round pi) :: Integral b => b
Programmera bilder!
Man kan även skapa bilder direkt genom programmering
Vi ska göra detta på en annan hemsida som heter http://code.world
Ett mycket enkelt program ser ut så här:
main = drawingOf(sol)
sol = circle(1)
circle
är en funktion som tar ett tal och ger tillbaka en bild av en cirkelsol
är en (konstant) funktion som ger tillbaka en bild av en cirkel med radien1
drawingOf
är en funktion som tar en bild och ger tillbaka ett programmain
är en speciell (huvud) funktion som alltid måste finnas och ge tillbaka ett program
En annan skillnad från tidigare är att man skriver värden till funktionen inom paranteser med comma (,
) emellan
Värdet som kommer ut från main
är resultatet när man kör programmet.
Programmera bilder!
main = drawingOf(sol)
sol = circle(1)
- Testa att skiva in programmet
- Testa att köra programmet genom att trycka på
Run
(kör) - Vad är resutatet från programmet när du kör det?
Mer avancerade program - Solsystemet
Nu är vi redo för mer avancerade program.
Vi ska skriva ett program som simulerar solsystemet!
Till vår hjälp har vi lite dokumentation
- code.world Guide - hittar du bland knapparna längst ned
- code.world API
Färger
Först ska vi byta ut cirkeln mot en fylld cirkel. Funktionen solidCircle
(fylld cirkel) gör det.
sol = solidCircle(1)
Det finns en funktion colored
som tar en bild och en färg och skickar ut bilden med en ny färg.
Skicka cirkeln och färgen yellow
(gul) så får vi en färgad cirkel.
sol = colored( solidCircle(1) , yellow)
Testa detta!
Olika typer
Vad är händer egentligen när man anropar funktionen colored()
?
colored()
tar två värden (värden in till en funktion kallas ofta parametrar
):
- En bild (
Picture
) som i det här fallet ärsolidCircle(1)
- En färg (
Color
) som i det här fallet äryellow
Det värde som kommer ut från funktionen är en ny bild (Picture
) dvs. en gul fylld cirkel
Det är viktigt att alltid skicka in parametrar (värden) av rätt typ i funktioner annars kommer programmet inte att fungera
Olika typer
Testa exempelvis att byta plats på parametrar bild och färg i funktionen colored()
och kör programmet (Run
).
sol = colored(yellow, solidCircle(1))
Line 3, Column 15: error:
• Couldn't match expected type Picture with actual type Color
• In the expression: yellow
In the parameter(s) of colored, namely
(yellow, solidCircle (1))
In the expression: colored (yellow, solidCircle (1))
Line 3, Column 23: error:
• Couldn't match expected type Color with actual type Picture
• In the expression: solidCircle (1)
In the parameter(s) of colored, namely
(yellow, solidCircle (1))
In the expression: colored (yellow, solidCircle (1))
Detta är ett felmeddelande som talar om att funktionens första värde yellow
inte passar (match) med den förväntade typen,
Picture
. Det samma gällar andra värdet som inte matchar typen Color
. Det står också på vilken rad felet är.
Olika typer (2)
Det är inte speciellt konstigt att ordningen har stor betydelse inom programmering.
Man kan tex inte byta ordning på a - b
till b - a
och förvänta sig samma resultat.
Det gäller alla matematiska funktioner om dom inte är kommunativa som tex. multiplikation är.
Fler funktioner och typer
Liksom funktionen colored
finns många andra funktioner som förändrar bilden som skickas in.
Funktionen translated
tar en bild som första parameter, sedan ett tal (Number
), sedan ytterligare ett tal. Resultatet är
en ny bild där bilden är flyttad (translated
) i x och y led.
Så här skriver man vilka typer av parametrar som funktionen tar. Sista typen anger typen av resultatet.
translated :: Picture -> Number -> Number -> Picture
Man kan få hjälp med vilka typer en funktion tar i code.world.
Skriv tex. tr
och sedan Ctrl
+Space
Titta också på funktionen colored
genom att skriva co
och Ctrl
+Space
Planeter
Nu är det dags för solen att få sällskap av nya planeter.
Vi skapar fler planeter vid sidan om solen i solsystemet
På wikipedia kan man se att planeternas storlek anges om andel av jorden storlek.
Vi bestämmer att jorden storlek ska vara 20%
av solens.
size_sun = 1.0
size_earth = 0.2 * size_sun
Sedan skapar vi planeter ...
merkurius = solidCircle(size_earth*0.4)
venus = solidCircle(size_earth*0.4)
jorden = solidCircle(size_earth)
mars = solidCircle(size_earth*1.5)
Testa att rita ut en av planeterna.
main = drawingOf(venus)
Flera planeter
Hur kan vi rita ut flera planeter samtidigt?
Det finns en funktion &
som tar två bilder och skickar ut summan (båda) dessa bilder som en ny bild som resultat
& :: Picture -> Picture -> Picture
Testa genom att rita ut både solen och jorden
main = drawingOf( sol & jorden )
Vad hände? Jorden syns inte. Solen och jorden skrivs ut på samma plats och skriver över varandra
Flera planeter (2)
Det här kan vi fixa genom att flytta ut jorden från centrum av ritytan
Funktionen translated
flyttar på bilder
translated :: Picture -> Number -> Number -> Picture
translated
tar alltså en bild och två tal som flyttar bilden i x
och y
led
Så vi kan fixa överritningen genom att flytta jorden ut 4 solradier i x-led från solens centrum
jorden = translated(solidCircle(size_earth), 4 * size_sun, 0)
Ett solsystem
Så här ser inre delen av solsystemet ut ...
program = drawingOf(solsystem)
size_sun = 1.0
size_earth = 0.2 * size_sun
sol = colored(solidCircle(1), yellow)
merkurius = translated(solidCircle(size_earth), 2, 0)
venus = translated(solidCircle(size_earth), 3, 0)
jorden = translated(solidCircle(size_earth), 4, 0)
mars = translated(solidCircle(size_earth), 5, 0)
solsystem = sol & merkurius & venus & jorden & mars
Mer realistiskt solsystem
Vi vill skapa ett helt solsystem med olika planeter med olika storlek, färg och avstånd från solen.
För att underlätta programmering skapar vi en funktion som skapar en ny planet
planet(size, distance, color) = colored(translated(solidCircle(earth_size*size), earth_dist*distance, 0), color)
Storlek (size
), avstånd (distance
) och färg (color
) är in parametrar till funktionen. Resultatet är en bild på planeten
Funktionen använder inget nytt utan kombinerar bara dom funktioner som vi har använt tidigare.
Mer realistiskt solsystem
Men denna planet skapar funktion kan vi visa ett mer realistiskt solsystem
program = drawingOf(solsystem)
sol = colored (solidCircle(1), yellow)
earth_size = 0.2 -- sun has radius 1
earth_dist = 5.0 -- distance from center of sun
merkurius = planet(0.382, 0.387, yellow)
venus = planet(0.949, 0.72, yellow)
jorden = planet(1, 1, blue)
mars = planet(0.53, 1.52, red)
planet(size, distance, color) = colored(translated(solidCircle(earth_size*size), earth_dist*distance, 0), color)
solsystem = sol & merkurius & venus & jorden & mars
Planetrörelser
Skulle det inte vara coolt om man kunde få planeterna att röra sig kring solen också?
Det första vi måste göra är att byta ut programmet mot en animering. En animering är en bild som ändrar sig med avseende på tiden.
Om tiden (betecknat t
) skulle kunna vara en av parametrarna till solsystem
så skulle vi kunna rita ut hur solsystemet se ut vid olika tidpunkter.
Vi använder en funktion rotated
som tar en bild och en ytterligare parameter som anger hur mycket bilden ska rotera (i grader kring centrum av bilden)
Tiden som parameter
Så vi byter ut funktionen solsystem
med en ny funktion som tar en parameter t
:
solsystem(t) = sol
& rotated(merkurius,(10/0.241)*t)
& rotated(venus,(10/0.615)*t)
& rotated(jorden, 10*t)
& rotated(mars, (10/1.88)*t)
Vi kan köra programmet med olika värden på t
för att se hur det ser ut vid olika tidpunkter. Testa 0, 1, 2, ...
program = animationOf(solsystem(1))
t
Animera med tiden Men vi kan faktiskt få programmet att visa bilden för många olika värden på t
automatiskt dvs. animera bilden.
För det behöver vi en ny funktion animationOf
i stället för drawingOf
. Skillnaden är att animationOf
förväntar sig en parameter som är just den typ av funktion som vi precis har skapat.
animationOf :: (Number -> Picture) -> Program
Jämför med drawingOf
som bara tar en parameter av typen Picture
drawingOf :: Picture -> Program
Funktioner som parametrar
Funktioner är alltså också datatyper som kan användas som parametrar
Funktionen solsystem(t)
har redan rätt datatyp
solsystem :: Number -> Picture
animationOf
förväntar sig alltså en funktion som genererar en bild från ett tal som representerar tid.
animationOf
kommer att anropa denna funktion från t = 0
och framåt och skapa en animation.
Testa att byta ut main
mot den nya funktionen.
program = animationOf(solsystem)