Programmeerparadigma's-thema
Programmeerparadigma: functioneel programmeren
onderwerp 1
onderwerp 2
Lijsten (met Map, Fold, Filter)
Intro Lists Coen
Typ op de command line
elm-repl
REPL staat voor ... (google hier eens op).
We kunnen nu elm expressies intypen die dan direct geëvalueerd worden. Bijvoorbeeld:
42
Reactie:
42 : number
ofwel repl heeft gezien dat het resultaat 42 is en dat dit een getal (number) is.
Iets spannender (denk even na of je het antwoord van deze vermeningvuldiging makkelijk zelf zou kunnen berekenen)
42 * 38
Hee, je kunt het dus als rekenmachine gebruiken... Enfin, je bent waarschijnlijk niet heel erg verbaasd.
Inderdaad kun je met +, -, * en / optellen, aftrekken, vermenigvuldigen en delen.
Wat denk je dat er uitkomt als je
12 + 3 * 2
intypt? Houdt elm rekening met de volgorde van berekening? Wordt eerst de + of eerst * uitgevoerd? Ofwel: is dit gelijk aan ( 12 + 3 ) * 2 of juist aan 12 + ( 3 * 2) ? 0.0.0.1. Reken
Als je zelf de volgorde wil aangeven kun je er voor kiezen op de volgende manier te rekenen:
add 12 ( multiply 3 2 )
Hierbij worden er 2 dingen opgeteld (add): het eerste is een getal, het andere is de uitkomst van de vermenigvuldiging van 3 en 2 (multiply).
Stel er zou staan:
multiply ( add 12 3 ) 2
dan zou juist de uitkomst van 12+3 vermenigvuldigd worden met 2.
Nu is add een functie die normaalgesproken 2 parameters krijgt, maar in de taal elm kun je de parameters 1 voor 1 aan een functie voeren. De combinatie add 2 is een optelfunctie die al weet dat ie 2+.. gaat doen.
Nu zijn add en multiply al bestaande functies. Je kunt ook zelf nieuwe functies erbij programmeren. Met bijvoorbeeld
telTweeOpBij x = x + 2
wordt een nieuwe functie telTweeOpBij (deze naam kun je dus zelf kiezen) gedefinieerd door de al genoemde add 2 constructie (deze combinatie ís ook weer een functie). Na deze definitie kun je schrijven
telTweeOpBij 5
Komt hier uit wat je verwacht? Onthoud de functie telTweeOpBij, we komen hier op terug. 0.0.0.2. List
Typ eens in:
List.range 1 9
Resultaat:
[1,2,3,4,5,6,7,8,9] : List Int
Hee, hoe komt dat? Blijkbaar wordt er een lijstje van getallen opgebouwd.
Ook kun je aan een bestaande List met behulp van :: een element toevoegen:
42 :: (List.range 1 9)
en ook meerdere elementen kan
83 :: 42 :: (List.range 1 9)
Zoals je ziet zijn haakjes hier niet nodig. Op deze manier kun je een bestaande List verlengen. 0.0.0.3. En tel er 2 bij op...
Het leuke van Lists is dat je er berekeningen op kunt gaan toepassen. Stel we willen bij elk getal in de List het getal 2 optellen. We mappen dan de functie telTweeOpBij op elk getal uit de List:
List.map telTweeOpBij (List.range 1 9)
We hadden in plaats van telTweeOpBij ook kunnen schrijven
List.map ( \x -> x + 2 ) (List.range 1 9)
waarbij x -> x + 2 eigenlijk wil zeggen: wat er in gaat noemen we x en er komt uit x + 2. Je kunt dit vergelijken met een functie f in de wiskunde
f(x) = x + 2
alleen heeft de functie x -> x + 2 geen naam. Bij de definitie van telTweeOpBij een stukje terug hadden we de functie wel een naam gegeven (namelijk telTweeOpBij). Herhalen we nu die definitie maar dan met naam f in plaats van telTweeOpBij dan staat er:
f x = x + 2
Hee, da's bijna wiskunde!? We hebben nu dus 2 manieren gezien om een functie te definiëren
f x = x + 2 \x -> x + 2
De eerste definieert functie f en de tweede heeft geen naam, is anoniem. We noemen die tweede definitie dan ook een anonymous function.
Enfin, de constructie
List.map ( \x -> x + 2 ) (List.range 1 9)
is niet super-leesbaar. Je mag dit in elm ook anders opschrijven: Neem (List.range 1 9) en map op elk element de functie ( x -> x + 2 ). Dit ziet er dan uit als:
(List.range 1 9) |> List.map ( \x -> x + 2 )
In de repl moet je op het eind van een regel een backslash (\) typen (die staat hier door) om op de volgende regel door te gaan. 0.0.0.4. tut p.10
sum (filter (isOver 100) (map getCost records))
This code is difficult to read, because it resolves inside out. The pipe operator allows us to write such expressions in a more readable way: Using the pipe operator the complex example above would be written like:
records |> map getCost |> filter (isOver 100) |> sum
0.0.0.5. Lijsten creëren
Een andere manier om een List te creëren is
[ 3 , 5 , 6 , 8 ]
ofwel het simpelweg opsommen wat er in moet komen. Voorbeeld met woorden/strings er in:
[ "abra" , "ca" , "dabra" ]
Een bijzondere versie hiervan is het aanmaken van de lege List: een List zonder elementen
[]
Dat lijkt misschien niet zo bijzonder maar een lege lijst is bij het werken met lijsten net zo belangrijk als de getallen 0 en 1 bij het rekenen. 0.0.0.6. Tweedegraads vergelijking
Zoals je vast hebt geleerd kun je de zogenaamde tweedegraads vergelijking
a*x^2 + b*x + c = 0
oplossen door de discriminant D = b^2 - 4*a*c uit te rekenen. Als D kleiner dan 0 is is er geen reële oplossing, als D 0 of groter is zijn er 2 oplossingen (die voor D=0 aan elkaar gelijk zijn).
Definieer een functie discriminant die gegeven 3 getallen (inderdaad: a, b, c) de discrininant berekent:
discriminant a b c = (b * b) - (4 * a * c)
Voor de leesbaarheid is het goed hier de haakjes te zetten!
Zou 38x + 6 oplossingen hebben?
discriminant 38 12 6
geeft het antwoord. Dat wil zeggen als het antwoord groter dan 0 is zijn er 2 verschillende oplossingen voor x, als het antwoord 0 is is er 1 oplossing en als het antwoord begint met een - is er geen (reële) oplossing.
Merk op dat elm de discriminant ziet als een functie van het type
number -> number -> number -> number
wat wil zeggen dat de functie achtereenvolgens 3 parameters verwacht! Het is dus mogelijk voor tweedegraads vergelijkingen waarbij er géén getal (ofwel een 1) voor de x-kwadraat staat een alternatieve functie te definiëren om de discriminant te berekeningen (noemen de dit de eenheidsdiscriminant) door middel van
eenheidsdiscriminant = discriminant 1
wat dus eigenlijk betekent dat de eenheidsdiscriminant de normale discriminant is met voor de a vast 1 ingevuld. Dat wil zeggen dat
discriminant 1 10 2
gelijk moet zijn aan
eenheidsdiscriminant 10 2
Is dat ook zo?
vanaf hier zijn het nog losse opmerkingen waar nog iets mee moet...
Map
Fold
Filter 0.0.0.7. Vorm van een List
Als een List niet leeg is is ie van de vorm
element :: eenAndereList
Het element voorop noemen we ook wel het kop-element (head), dit wordt voor de rest (een iets kortere List genaamd eenAndereList) geplakt. De enige List die niet van deze vorm is is de lege List die we noteren als [].
vanaf hier zijn het nog losse opmerkingen waar nog iets mee moet...
[ 'y' 'u' ] 0.0.0.8. Lengte van een List (~recursiviteit)
Aangezien er heel vaak met Lists gewerkt wordt is het handig dat er verschillende manieren om een List te construeren: Je kunt de elementen opnoemen
[2,3,5,7,13,24,57,56,33,21,3,2]
Let op dat er een volgorde in zit en elementen dubbel kunnen voorkomen.
import Html exposing (text)
main =
text (toString (length (List.range 1 9)))
{-| Figure out the length of any list. To find the length of
list [6,6,6] we need to know that this devilish list is just
a convenient way to write (6 :: (6 :: (6 :: []))) where
the :: operator is putting an element on the front of a list.
Evaluation looks like this:
length (6 :: (6 :: (6 :: []))) 1 + (length (6 :: (6 :: []))) 1 + (1 + (length (6 :: []))) 1 + (1 + (1 + length [])) 1 + (1 + (1 + 0)) 1 + (1 + 1) 1 + 2 3
Stepping through evaluation like this can be really helpful for building intuition about recursive functions. -} length : List a -> Int length list =
case list of [] -> 0
first :: rest -> 1 + length rest
{- EXPLANATION
The 'length' function figures out the length of any list. We use the 'case' keyword to "pattern match" on the structure of the list.
Lists can either be empty [] or a pair of an element and a sublist like (1 :: []) or (1 :: (2 :: []))
To write 'length' we have two cases to think about:
1. If the list is empty, the length is zero. 2. If the list is not empty, the length is 1 more than however long the rest of the list is.
When you write a function like 'length', always pretend that you already succeeded. So when we need to know how long the rest of the list is, we can pretend 'length' is already done and use it! -}
0.0.0.9. Telgetallen 0.0.0.10. Fibonacci reeks? 0.0.0.11. Anonymous functions
\x -> x + 1
\x y -> x + y
0.0.0.12. Named functions
addOne : Int -> Int addOne x =
x + 1
In Elm all functions take exactly one argument and return a result. This result can be another function.