Welkom bij mijn intro over Functioneel programmeren. Dit heeft eerder in het abo forum gestaan, dus als je het daar al gelezen hebt, don't bother
Wat te vinden in deze introductie? (Klikbaar)
Wat is functioneel programmeren
Functioneel programmeren gaat over het programmeren in functies. Ik heb al veel mensen gehoord die dan roepen "ow functioneel programmeren. In mijn C++ programma kan ik ook functies maken".
Err... Als het hetzelfde was geweest had het wel gelijk geheten toch? 'Functioneel' in 'Functioneel programmeren' staat namelijk voor het wiskundige begrip van een functie.
Een wiskundige functie is niets meer tussen de relatie tussen de parameters en het antwoord. Een wiskundige functie zal dan ook altijd hetzelfde antwoord geven bij dezelfde parameters. Dit in tegenstelling tot de meeste functies in imperatieve talen (C, C++, PHP, Java ed.), welke een output geven naar gelang de systeemtijd/ een globale variabele en allerlei andere invloeden van buiten de functie.
Haskell
Haskell is de taal die ik zal gaan gebruiken in deze introductie. Er zijn ook allerlei andere functionele talen, waarvan veel van het gezegde ook op zal gaan. Voorbeelden hiervan zijn Clean en Miranda
Haskell is een functionele taal met enkele mooie eigenschappen. Zo is Haskell type safe en heeft het lazy evaluation.
Type safe
Type safe betekend dat overal de types van gecontroleerd worden. Als een functie bijvoorbeeld een getal nodig heeft, dan is het niet mogelijk om daar een zin aan mee te geven. Immers met een zin is niet op te tellen en andere rekenkundige operaties, dus vandaar dat het een fout zal geven bij het draaien.
Bij type safe languages wordt dus bij het compileren gecontroleerd of het type van de parameters wel correct is. Als dat uitgesteld zou zijn tot run-time (als de functie gedraait wordt) dan kan er altijd nog in een hoekje van je programma een type fout zitten.
Type safe is dus een extra veiligheidscheck
Lazy Evaluation
Het woord zegt het eigenlijk al. Luie evaluatie. Kort gezegd kun je zeggen dat er pas wat uitgerekend zal worden als het echt niet anders meer kan.
Een voordeel daarvan is dat je bijvoorbeeld oneindige resultaten gebruikt kunnen worden!
Ik kan bijvoorbeeld de kop van een oneindige lijst getallen gaan pakken. Dit omdat haskell de getallen die ik toch niet gebruik, nooit zal uitrekenen.
Helium
In dit verhaal zal ik de Helium compiler gaan gebruiken. Dit omdat het een distributie is die speciaal gericht is op onderwijs, gratis is en klein om te downloaden. Deze distributie is geschreven door Arjan van IJzendoorn.
Omdat Helium toegespitst is op onderwijs en nog redelijk nieuw is, ondersteunt het nog geen type classes (kom ik later op terug) en optimaliseerd het erg weinig. Voor real life applicaties kun je dan nu ook nog beter GHC gebruiken.
Types
Eerder vertelde ik al dat Types belangrijk zijn voor de veiligheid van de applicatie. Alle variabelen moeten een type hebben voordat ze kunnen worden gecontroleerd. Omdat alles gecontroleerd wordt, moet dus alles moet een type hebben
In Haskell heb je 6 basistypes. Int, Float, Bool, , Char, Lijsten en Tupels
Int
Een Int is een geheel getal. Getallen als 3, 39, 4999 dus.
Float
Een gebroken getal. Getallen als 4.3, pi, 49999.0 ed
Bool
Een waarheidwaarde. Kan alleen True of False zijn.
Char
Een char is een teken. Bijvoorbeeld 'a', 'z', '2' of '@'. Een char staat altijd tussen enkele quotes
Lijsten
Een lijst is een verzameling van objecten van hetzelfde type. De notering ervan is door het omsluiten van vierkanten haken en scheiden met komma's.
Voorbeelden zijn dus [1,2,3,4], [True, False, True] en [4.3, pi] Deze verzamelingen mogen een variabele lengte hebben
Tupels
Een tupel is een beetje het omgekeerde van een lijst. Een tupel is een verzameling van een vaste lengte, zonder beperking van het type.
De notatie is dmv ronde haken en gescheiden met een komma
Voorbeelden (1, True), (True, 3.0, 1), (1,1,1)
Strings
Strings zijn geen echt datatypes in Haskell. Strings zijn simpelweg lijsten van Char's. Je kunt dus alle functies die je op lijsten kan toepassen, ook op Strings toepassen.
Functies
Nu we de basistypes weten, willen we nu gaan beginnen met het echte werk. Het creëren van die zgn. wiskundige functies. Laten we beginnen met een voorbeeld, welke ik dan zal uitleggen.
Een functie hoort altijd uit 2 delen te bestaan. Bovenaan zien we de signature en daaronder zijn definitie
Signature
De signature vertelt hoe de functie in elkaar zit. De signature van
Dat klopt ook met de berekening van de discriminant als ik het goed heb
Andere voorbeelden van signatures zijn
Odd bekijkt of een Int (zijn enige parameter) oneven is. Als dat zo is, zal odd een True opleveren, anders een False
isUpper kijkt of een Char (zijn enige parameter) een hoofdletter is. Als dat zo is, zal isUpper een True opleveren, anders een False
Neemt het product over een lijst van Ints. Bijvoorbeeld [1,3,5,8] zal (1*3*5*8) = 120 opleveren.
Definitie
In de definitie staat de werkelijke code, wat de functie zal gaan uitvoeren.
Dit is op te delen in 2 delen, gescheiden door het = teken.
Links van het = teken zien we staan
Rechts van het = teken staat
Hoe dit te maken in Helium?
Als je Helium geïnstalleerd hebt, krijg je als het goed is een snelkoppeling op je bureaublad (in Windows iig
). Start deze op en er zal een scherm verschijnen.

Zoals je ziet bestaat het scherm uit 2 delen. Het uitvoerscherm (het grote witte vlak) en het aanroepveld (het textveld onderin)
In het aanroepveld, roep je je functies aan en de output zal je zien in het uitvoerscherm.
Als je een functie wil definieren in Helium, zul je een Texteditor moeten pakken. Een die veel gebruikt wordt is ConTeXT. Deze ondersteunt nl. Helium syntax coloring, met de language file op de helium website.
Maar goed. Je opent dus de tekst editor.
Dan voer je je functie in. Neem bijvoorbeeld de functie
Vervolgens open je je juist gecreëerde hs file in Helium. Dit kan door in het menu File te kiezen voor Open Module. Dan kies je je hs file.
Helium zal dan je hs file gaan compileren. Een lvm file zal ontstaan, als alles goed gaat.
Om dan vervolgens discriminant aan te roepen, typ je 'discriminant' en daarachter 3 getallen (Int's) gescheiden dmv een spatie, in het aanroepveld. De uitvoer zal daarna in het uitvoerscherm terecht komen.
Predefined functies
Helium bevat een hoop voorgefinieerde functies. Hier staan ze allemaal Hieronder zal ik er een paar noemen zodat je zelf wat kunt gaan programmeren
Int functies
Float functies
Zie +, -, *, ==, >= en <= bij de Int functies, maar zet er dan een punt achter
Dit omdat Helium geen overloading (verschillende functies met gelijke namen) ondersteunt
Bool functies
Lijsten
Enige functies op Strings en Tupels mogen jullie zelf opzoeken in http://www.cs.uu.nl/~afie/helium/docs/TourOfPrelude.html .
End of story
Hiermee gaat het los. Ga zelf aan de slag met dingentjes proberen ed.
Volgende keer zal ik dieper overal op in gaan. Ik zal dan gaan kijken hoe lijsten in elkaar zitten, wat functies nou precies doen, wat partieel parameteriseren is en operator's gaan behandelen.
Vragen/opermkingen kunnen via ICQ/Mail en hier
Wat te vinden in deze introductie? (Klikbaar)
Wat is functioneel programmeren
Functioneel programmeren gaat over het programmeren in functies. Ik heb al veel mensen gehoord die dan roepen "ow functioneel programmeren. In mijn C++ programma kan ik ook functies maken".
Err... Als het hetzelfde was geweest had het wel gelijk geheten toch? 'Functioneel' in 'Functioneel programmeren' staat namelijk voor het wiskundige begrip van een functie.
Een wiskundige functie is niets meer tussen de relatie tussen de parameters en het antwoord. Een wiskundige functie zal dan ook altijd hetzelfde antwoord geven bij dezelfde parameters. Dit in tegenstelling tot de meeste functies in imperatieve talen (C, C++, PHP, Java ed.), welke een output geven naar gelang de systeemtijd/ een globale variabele en allerlei andere invloeden van buiten de functie.
Haskell
Haskell is de taal die ik zal gaan gebruiken in deze introductie. Er zijn ook allerlei andere functionele talen, waarvan veel van het gezegde ook op zal gaan. Voorbeelden hiervan zijn Clean en Miranda
Haskell is een functionele taal met enkele mooie eigenschappen. Zo is Haskell type safe en heeft het lazy evaluation.
Type safe
Type safe betekend dat overal de types van gecontroleerd worden. Als een functie bijvoorbeeld een getal nodig heeft, dan is het niet mogelijk om daar een zin aan mee te geven. Immers met een zin is niet op te tellen en andere rekenkundige operaties, dus vandaar dat het een fout zal geven bij het draaien.
Bij type safe languages wordt dus bij het compileren gecontroleerd of het type van de parameters wel correct is. Als dat uitgesteld zou zijn tot run-time (als de functie gedraait wordt) dan kan er altijd nog in een hoekje van je programma een type fout zitten.
Type safe is dus een extra veiligheidscheck
Lazy Evaluation
Het woord zegt het eigenlijk al. Luie evaluatie. Kort gezegd kun je zeggen dat er pas wat uitgerekend zal worden als het echt niet anders meer kan.
Een voordeel daarvan is dat je bijvoorbeeld oneindige resultaten gebruikt kunnen worden!
Ik kan bijvoorbeeld de kop van een oneindige lijst getallen gaan pakken. Dit omdat haskell de getallen die ik toch niet gebruik, nooit zal uitrekenen.
Helium
In dit verhaal zal ik de Helium compiler gaan gebruiken. Dit omdat het een distributie is die speciaal gericht is op onderwijs, gratis is en klein om te downloaden. Deze distributie is geschreven door Arjan van IJzendoorn.
Omdat Helium toegespitst is op onderwijs en nog redelijk nieuw is, ondersteunt het nog geen type classes (kom ik later op terug) en optimaliseerd het erg weinig. Voor real life applicaties kun je dan nu ook nog beter GHC gebruiken.
Types
Eerder vertelde ik al dat Types belangrijk zijn voor de veiligheid van de applicatie. Alle variabelen moeten een type hebben voordat ze kunnen worden gecontroleerd. Omdat alles gecontroleerd wordt, moet dus alles moet een type hebben
In Haskell heb je 6 basistypes. Int, Float, Bool, , Char, Lijsten en Tupels
Int
Een Int is een geheel getal. Getallen als 3, 39, 4999 dus.
Float
Een gebroken getal. Getallen als 4.3, pi, 49999.0 ed
Bool
Een waarheidwaarde. Kan alleen True of False zijn.
Char
Een char is een teken. Bijvoorbeeld 'a', 'z', '2' of '@'. Een char staat altijd tussen enkele quotes
Lijsten
Een lijst is een verzameling van objecten van hetzelfde type. De notering ervan is door het omsluiten van vierkanten haken en scheiden met komma's.
Voorbeelden zijn dus [1,2,3,4], [True, False, True] en [4.3, pi] Deze verzamelingen mogen een variabele lengte hebben
Tupels
Een tupel is een beetje het omgekeerde van een lijst. Een tupel is een verzameling van een vaste lengte, zonder beperking van het type.
De notatie is dmv ronde haken en gescheiden met een komma
Voorbeelden (1, True), (True, 3.0, 1), (1,1,1)
Strings
Strings zijn geen echt datatypes in Haskell. Strings zijn simpelweg lijsten van Char's. Je kunt dus alle functies die je op lijsten kan toepassen, ook op Strings toepassen.
Functies
Nu we de basistypes weten, willen we nu gaan beginnen met het echte werk. Het creëren van die zgn. wiskundige functies. Laten we beginnen met een voorbeeld, welke ik dan zal uitleggen.
code:
1
2
| discriminant :: Int -> Int -> Int -> Int discriminant a b c = b*b - 4*a*c |
Een functie hoort altijd uit 2 delen te bestaan. Bovenaan zien we de signature en daaronder zijn definitie
Signature
De signature vertelt hoe de functie in elkaar zit. De signature van
discriminant
zegt 4x Int met pijltjes ertussen. Dit betekend dat hij 3 Int waardes aanneemt als parameter en één Int als resultaat zal opleveren.Dat klopt ook met de berekening van de discriminant als ik het goed heb
Andere voorbeelden van signatures zijn
code:
1
| odd :: Int -> Bool |
Odd bekijkt of een Int (zijn enige parameter) oneven is. Als dat zo is, zal odd een True opleveren, anders een False
code:
1
| isUpper :: Char -> Bool |
isUpper kijkt of een Char (zijn enige parameter) een hoofdletter is. Als dat zo is, zal isUpper een True opleveren, anders een False
code:
1
| product :: [Int] -> Int |
Neemt het product over een lijst van Ints. Bijvoorbeeld [1,3,5,8] zal (1*3*5*8) = 120 opleveren.
Definitie
In de definitie staat de werkelijke code, wat de functie zal gaan uitvoeren.
Dit is op te delen in 2 delen, gescheiden door het = teken.
Links van het = teken zien we staan
discriminant a b c
. Dit komt overeen met onze signature. We hebben nu alleen elke parameter een naam gegeven (namelijk a voor de eerste int, b voor de tweede int en c voor de derde)Rechts van het = teken staat
b*b - 4*a*c
. Dit doet gewoon wat je er van verwacht, namelijk b kwadrateren en daar 4 maal ac van af trekken. Het antwoord hiervan, zal het antwoord van de functie wordenHoe dit te maken in Helium?
Als je Helium geïnstalleerd hebt, krijg je als het goed is een snelkoppeling op je bureaublad (in Windows iig

Zoals je ziet bestaat het scherm uit 2 delen. Het uitvoerscherm (het grote witte vlak) en het aanroepveld (het textveld onderin)
In het aanroepveld, roep je je functies aan en de output zal je zien in het uitvoerscherm.
Als je een functie wil definieren in Helium, zul je een Texteditor moeten pakken. Een die veel gebruikt wordt is ConTeXT. Deze ondersteunt nl. Helium syntax coloring, met de language file op de helium website.
Maar goed. Je opent dus de tekst editor.
Dan voer je je functie in. Neem bijvoorbeeld de functie
discriminant
en zet deze in de textfile. Sla dan de file op als [naamMetHoofdletter].hsVervolgens open je je juist gecreëerde hs file in Helium. Dit kan door in het menu File te kiezen voor Open Module. Dan kies je je hs file.
Helium zal dan je hs file gaan compileren. Een lvm file zal ontstaan, als alles goed gaat.
Om dan vervolgens discriminant aan te roepen, typ je 'discriminant' en daarachter 3 getallen (Int's) gescheiden dmv een spatie, in het aanroepveld. De uitvoer zal daarna in het uitvoerscherm terecht komen.
Predefined functies
Helium bevat een hoop voorgefinieerde functies. Hier staan ze allemaal Hieronder zal ik er een paar noemen zodat je zelf wat kunt gaan programmeren
Int functies
code:
1
2
3
4
5
6
7
8
9
10
| +, - * Plus, min en vermenigvuldigen == (Int -> Int -> Bool) vergelijk 2 Ints met elkaar >= (Int -> Int -> Bool) kijkt of de Int links groter of gelijk aan de Int rechts is <= (Int -> Int -> Bool) kijkt of de Int links kleiner of gelijk aan de Int rechts is NB. Je hebt ook de versies zonder = bij >= en <=, deze controleren namelijk niet op gelijkheid max (Int -> Int -> Int) geeft de grootste van twee Ints terug min (Int -> Int -> Int) geeft de kleinste van twee Inst terug odd (Int -> Bool) geeft aan of de Int oneven is even (Int -> Bool) geeft aan of de Int even is |
Float functies
Zie +, -, *, ==, >= en <= bij de Int functies, maar zet er dan een punt achter
Dit omdat Helium geen overloading (verschillende functies met gelijke namen) ondersteunt
Bool functies
code:
1
2
3
| not (Bool -> Bool) draai de waarheidswaarde om. False wordt True, True wordt False && (Bool -> Bool -> Bool)logische AND || (Bool -> Bool -> Bool)logische OR |
Lijsten
code:
1
2
3
4
5
6
7
8
9
10
11
12
13
| head Geef het eerste element van de lijst tail Geef alle elementen van de lijst, behalve het eerste element last Geef het laaste element van de lijst map Voer een functie uit op een lijst Aanroep gaat volgens map functie lijst, waarbij functie op elk element van de lijst uitgevoerd zal worden en het resultaat dus een lijst met de antwoorden zal zijn filter filter alle elementen uit de lijst die niet aan de voorwaarde voldoen Aanroep gaat volgens filter voorwaarde lijst, waarbij de voorwaarde een functie van X -> Bool is, waarbij X gelijk moet zijn aan de elementen in de lijst null Geeft true als de lijst leeg is length Geeft een integer met de lengte van de lijst |
Enige functies op Strings en Tupels mogen jullie zelf opzoeken in http://www.cs.uu.nl/~afie/helium/docs/TourOfPrelude.html .
End of story
Hiermee gaat het los. Ga zelf aan de slag met dingentjes proberen ed.
Volgende keer zal ik dieper overal op in gaan. Ik zal dan gaan kijken hoe lijsten in elkaar zitten, wat functies nou precies doen, wat partieel parameteriseren is en operator's gaan behandelen.
Vragen/opermkingen kunnen via ICQ/Mail en hier
[ Voor 104% gewijzigd door Glimi op 02-06-2003 15:47 ]