Module 4 – Softwareontwikkeling PSD’s maken Hoofdstuk 2 2.8 Samenvatting ff Een algoritme is een set opdrachten om een berekening of een handeling stapsgewijs in een bepaalde volgorde uit te voeren. ff Een PSD (Programma Structuur Diagram) is een schema dat de samenhang van instructies binnen een programma weergeeft. Een PSD wordt getekend met rechthoeken, driehoeken en tekst en is te beschouwen als voorbereiding op het coderen in een programmeertaal. ff Bij PSD's zijn er drie basisstructuren: sequentie, iteratie en selectie. ff Sequentiële opdrachten worden van boven naar beneden één voor één uitgevoerd. ff Bij een iteratie worden bepaalde opdrachten herhaald; er zijn twee soorten iteraties: iteratie met controle achteraf en iteratie met controle vooraf. ff Bij een selectie splitst het programmaverloop zich op grond van de vraag of wel of niet aan een bepaalde voorwaarde is voldaan. ff Het achteraf nalopen van een PSD is noodzakelijk om inzicht te krijgen in de werking ervan en om na te gaan of het schema juist is. Dat nalopen wordt wel een ooggetuigenverslag genoemd. Module 4 – Softwareontwikkeling Imperatief programmeren HOOFDSTUK 3 Imperatief programmeren 3.1 Stapsgewijs programmeren De programmeertalen die tot nu toe genoemd zijn, zijn imperatieve of procedurele programmeertalen. Imperatief programmeren is het stapsgewijs in code omschrijven wat een programma moet doen, net als een algoritme of een PSD. Interessant ar tikel in Computerwor ld: ‘25 Fatale program meerf outen’. Informatie Bij het uitleggen van een programma of algoritme wordt vaak pseudocode gebruikt. Pseudocode is geen code van een echte programmeertaal, maar is alleen maar bedoeld om dingen eenvoudiger uit te leggen. In dit hoofdstuk gebruiken we ook pseudocode. Vaak wordt bij pseudocode := gebruikt om aan te geven dat een variabele een waarde krijgt. 3.2 If Then Else Bij imperatief programmeren omschrijf je in een code stapsgewijs wat een programma moet doen, net als een algoritme en PSD. Wanneer een PSD selecties (keuzes) bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk. Denk nog maar even terug aan dit schema: Als je imperatief gaat programmeren, krijg je veel met zulke keuzes of voorwaardes te maken. Hiertoe zijn er de ‘if then’-statements en de ‘else’-statements. Voorbeeld: toegangsprijs We kijken weer even naar het voorbeeld dat de procedure beschrijft voor het bepalen van de toegangsprijs voor een pretpark. 220 Voorbeeld ALS leeftijd < DAN toegang := 5 ANDERS ALS leeftijd DAN toegang := ANDERS toegang := EINDE-ALS EINDE-ALS 12 euro >= 60 5 euro 10 euro 221 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 De code wordt sequentieel van boven naar beneden afgewerkt. Je ziet dat er allerlei keuzes gemaakt moeten worden. Uiteindelijk is het resultaat dat mensen jonger dan 12 en ouder dan, of net zo oud als 60, voor de toegang 5 euro betalen; de overige leeftijden betalen 10 euro. 3.4 Booleaanse expressies In de eerder gegeven voorbeelden heb je (misschien onbewust) al kennis gemaakt met Booleaanse expressies. Niet elke If heeft een Else, maar wel elke Else een If Informatie Het if-statement is niet onlosmakelijk verbonden aan het else-statement; het is niet noodzakelijk dat elk if-statement ook een else-statement heeft. Aan de andere kant hoort elk else-statement wel bij een if-statement. De naam Booleaans komt van de wiskundige en filosoof George Boole, die het redeneren in waarden als true en false heeft geïntroduceerd in de 19e eeuw. Een Booleaanse expressie is een expressie die de waarden true of false aan kan nemen. Informatie Merk op dat alle operaties binnen een statement iets naar rechts zijn geplaatst. Dit principe noemen we inspringen. In de meeste programmeertalen zal het programma ook werken als je dit niet doet, maar het is belangrijk om het wel te doen, omdat de code er overzichtelijker van wordt. Symbolen Booleaanse logica 3.3 Lus Wanneer een programma een iteratie (herhaling) bevat, zeggen we ook wel dat er sprake is van een lus (of 'loop' in het Engels). De operaties die in een lus zitten worden namelijk uitgevoerd zolang aan een bepaalde voorwaarde voldaan wordt. De meest bekende is de while-lus, hieronder aangegeven als 'ZOLANG'. Hier zie je een voorbeeld van een lus: Voorbeeld getal := 0 ZOLANG getal < 3 getal := getal + 1 EINDE-ZOLANG In dit voorbeeld wordt een getal net zolang verhoogd totdat het niet meer kleiner is dan 3. Met andere woorden: de lus stopt zodra het getal 3 is geworden. Oneindige lus Wanneer de voorwaarde van een while-lus altijd waar blijft, zal de lus niet vanzelf stoppen (het programma 'blijft hangen'). Dit noem je een 'oneindige lus'. Meestal is dit niet de bedoeling en heeft de programmeur een fout in de voorwaarde gemaakt, of ergens anders, of allebei. 222 De voorwaarde van een if-statement is een Booleaanse expressie. Als die de waarde true heeft, wordt de direct onderliggende code uitgevoerd en niet de code onder het elsestatement, als dit aanwezig is. Als de Booleaanse expressie de waarde false heeft, is het andersom. Symbool De symbolen in onderstaande tabel worden vaak gebruikt in de Booleaanse expressies van programmeertalen. Naam Gebruik > groter-dan x > y is true als x groter is dan y < kleiner-dan x < y is true als x kleiner is dan y >= <= not and or xor groter-dan-ofgelijk-aan kleiner-dan-ofgelijk-aan not and or exclusive or x >= y is true als x groter is dan y of net zo groot x <= y is true als x kleiner is dan y of net zo groot not x is true als x false is en andersom; de Booleaanse waarde van x wordt geïnverteerd x and y is true als x en y beide true zijn x or y is true als x of y true is (of als ze allebei true zijn) x xor y is true als x of y true is (en niet als ze allebei true zijn) Voorbeeld Misschien had je het al opgemerkt: de code van de toegangsprijs van het pretpark kunnen we met deze logica korter omschrijven: ALS leeftijd < 12 OF leeftijd >= 60 DAN toegang := 5 euro ANDERS toegang := 10 euro EINDE-ALS 223 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Wijze van weergeven Bij het opstellen van een Booleaanse expressie moet je zorgvuldig te werk gaan. Een verschil in de volgorde van de expressie kan bijvoorbeeld hele andere resultaten opleveren. Het is daarom veilig om delen van je expressie te voorzien van haakjes: '(' en ')'. Door delen tussen haakjes te zetten, geef je aan dat deze als geheel geïnterpreteerd moeten worden. Waarheidstabel Voorbeeld Voorbeeld van een functie die zou werken in C, C++, Java en C#: int som(int getal1, int getal2) { return getal1 + getal2; } Informatie De volgende expressie: Als je niet de juiste grammaticaregels van de programmeertaal gebruikt in je code, dan maak je een syntactische fout (Engels: 'syntax error'). Zou je bijvoorbeeld de haakjes vergeten in de code hierboven, dan krijg je hier een foutmelding bij. (x EN y) OF z is niet hetzelfde als: x EN (y OF z) Je kunt dit gemakkelijk zien door voor deze expressies een waarheidstabel te maken. Dit is een tabel waarin je aangeeft wanneer de expressie true of false is bij welke waarden. Hieronder zijn van beide expressies de verschillende waarheidstabellen weergegeven. (x EN y) OF z x EN (y OF z) x y z Waarde van expressie x y z Waarde van expressie true true true true true true true true true false false false true false false false true true true false true false true false true false true true true true false false false false true true false false false false Informatie true true true false true false true false true false true true true false false false false false true false false false false false Meestal worden in waarheidstabellen de termen true en false vervangen door respectievelijk 1 en 0. Dit kan zo'n tabel een stuk beknopter maken. 3.5 Syntax 224 De syntaxis (Engels: syntax) is de grammatica van een programmeertaal en beschrijft hoe de code er in een bepaalde programmeertaal uit moet komen te zien, wat de grammaticaregels zijn. Elke programmeertaal heeft een eigen syntax, maar sommige programmeertalen hebben onderling veel syntactische overeenkomsten, zoals C, C++, Java en C#. De reden dat veel programmeertalen een overeenkomstige syntax hebben, is dat men het de programmeurs makkelijker wil maken een andere taal te leren. 3.6 Variabelen Een variabele is een element dat een bepaalde waarde aan kan nemen. Een voorbeeld van een variabele is de toegangsprijs van een pretpark. De waarde die een variabele kan hebben, is afhankelijk van het type van de variabele. Er zijn verschillende typen die voorkomen in verschillende programmeertalen, maar doorgaans zijn de volgende typen aanwezig: ff Geheel getal Dit wordt een integer genoemd, vaak afgekort tot int. '42' is bijvoorbeeld een heel getal. ff Kommagetal Dit wordt vaak een double of float genoemd. Bijvoorbeeld: '109.75'. In veel programmeertalen moet je deze getallen op z'n Engels noteren, met een decimale punt. ff Karakter Dit noemt men een character of kortweg char (spreek uit: kar). Een karakter kan elk ANSI-, EBCDIC- of Unicode-teken zijn, bijvoorbeeld '$'. ff Boolean Dit wordt ook wel bool genoemd. Een boolean kan alleen maar true of false zijn. Sommige programmeertalen (waaronder C en C++) laten hier ook gehele getallen in toe, waarbij 0 staat voor false en alle andere getallen voor true. ff String Een apart soort variabele is de string. Dit is een reeks karakters, bijvoorbeeld 'tijger' of ‘fietswiel123’. 225 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Hoofdstuk 3 3.7 Arrays De meeste programmeertalen bieden de mogelijkheid om arrays (Nederlands: lijst) te maken. Een array is een geordende rij van waardes van hetzelfde type. Zo kun je bijvoorbeeld een array van gehele getallen maken. De waardes van een array zijn genummerd; het volgnummer noemen we de index. In veel programmeertalen begint de indexering van een array met 0 (en niet met 1). Voorbeeld rij_van_getallen := [32, 59, 825, 9042] De indexering: 0 32 1 59 2 825 3 9042 3.8 Functies Functies zijn eerder in dit hoofdstuk al eens genoemd. Je kunt deze gebruiken als je een bepaald stukje code vaker wilt gebruiken zonder dat je de code opnieuw hoeft te schrijven. Het maakt je programma een stuk overzichtelijker. Met een functie maak je een soort 'subprogrammaatje'. Als in het programma de naam van een functie staat, wordt daar het subprogrammaatje dat erbij hoort, uitgevoerd. We noemen dat het aanroepen van de functie. Van een functie moeten de volgende zaken worden gedefinieerd: ff De naam Elke functie moet een unieke naam hebben. Sommige programmeertalen laten toe dat namen van functies vaker voorkomen, zolang de parameters verschillen. Dit principe noem je overloading. ff De parameters Dit zijn speciale variabelen die een waarde krijgen bij het aanroepen van de functie. Zo kan de functie werken met de meegegeven waarden. Een functie kan nul of meer parameters hebben. 226 ff De body Dit is de code die uitgevoerd wordt als de functie aangeroepen wordt. Voorbeeld Voorbeeld som(int getal1, int getal2) : int int optelling := getal1 + getal2 return optelling rij_van_getallen waarde: ff Het returntype Dit is het waarde-type dat de functie na uitvoeren oplevert. Een functie kan maar één returntype hebben. Deze waarde wordt op de plek van de aanroep verder gebruikt. In dit voorbeeld wordt voor elke variabele, het type van de variabele gespecificeerd (net als bij de meeste programmeertalen). De code: index: Module 4 – Softwareontwikkeling Imperatief programmeren De functie 'som' telt twee getallen van het type 'int' bij elkaar op en geeft het resultaat (wat hier de naam 'optelling' heeft gekregen) als int terug. De termen 'getal1' en 'getal2' zijn de parameters en worden gescheiden door een komma. We kunnen nu naar deze functie refereren, of anders gezegd: we kunnen deze functie aanroepen en het resultaat in een andere variabele stoppen. Bijvoorbeeld: Voorbeeld int getalA := som(10, 20) int getalB := som(getalA, 70) int getalC := som(som(50, 50), getalB) Als bovenstaande code uitgevoerd zou worden zou na afloop getalA de waarde 30 hebben, getalB 100 en getalC 200. Je ziet: na een aanroep van een functie kan het resultaat precies zo worden behandeld als het returntype van de functie. In dit geval is het returntype van de functie een integer en kun je dus dit resultaat direct weer gebruiken, zoals op de derde regel hier ook gebeurt. 3.9 Parameters en argumenten We hebben eerder aangegeven dat getal1 en getal2 de parameters zijn van de functie 'som'. Bij het aanroepen worden deze parameters ingevuld door, wat we noemen, de argumenten. Een argument is een waarde die meegegeven wordt bij een functieaanroep. We herhalen het voorbeeld uit de vorige paragraaf: 227 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Voorbeeld int getalA := som(10, 20) int getalB := som(getalA, 70) int getalC := som(som(50, 50), getalB) Als bij het aanroepen van een functie een variabele meegegeven wordt - zoals in ons voorbeeld op de tweede regel het geval is - dan wordt niet de variabele zelf meegestuurd, maar de waarde van de variabele. Omdat de waarde wordt meegegeven, verandert de variabele zelf dus niet na het aanroepen van een methode, ongeacht wat de methode met de parameters doet. Deze functie heeft geen parameters en geen returnwaarde. Bovendien wordt er een andere functie aangeroepen met de naam 'print', waarbij een string als argument wordt meegegeven. 3.11 Recursie Wanneer een patroon zich binnen zichzelf herhaalt is er sprake van recursie. De figuur hieronder is bijvoorbeeld een recursief figuur, omdat het patroon - een twee keer zo klein vierkantje in de hoek linksboven - zich herhaalt. Denk ook maar aan het resultaat dat je ziet als je een spiegel tegenover een andere spiegel houdt. Ook binnen de wereld van het programmeren is recursie een bekend en belangrijk fenomeen. Sommige programmeerproblemen zijn namelijk beter of uitsluitend met recursie op te lossen. Binnen het programmeervak spreekt men van recursie als de body van een functie een aanroep van deze zelfde functie bevat, met andere woorden: als een functie zichzelf aanroept. We proberen dit in het volgende voorbeeld te verduidelijken. Voorbeeld We definiëren eerst een functie (die eigenlijk niets zinnigs doet): Voorbeeld zomaarEenFunctie(int eenGetal) : int eenGetal := 999 return 0 Vervolgens roepen we deze aan: Voorbeeld mijnEersteGetal := 5 mijnTweedeGetal := zomaarEenFunctie(mijnEersteGetal) Na het zogenaamd uitvoeren van deze code zal mijnEersteGetal nog altijd de waarde 5 hebben en mijnTweedeGetal de waarde 0. 3.10 Void Alle voorbeeldfuncties tot nu toe hadden een returnwaarde, namelijk een int. Maar een functie hoeft niet per se een returnwaarde te hebben. Dat geef je aan met het returntype void. Void is eigenlijk geen variabele-type, maar is eerder de afwezigheid van een waarde. Letterlijk vanuit het Engels vertaald betekent void dan ook 'leegte'. Hieronder is een voorbeeldfunctie zonder returnwaarde gedefinieerd: Voorbeeld zegHallo() : void print(“hallo”) 228 Een recursief figuur. Hieronder zie je een voorbeeld van een recursieve functie: Voorbeeld mijnFunctie() : void mijnFunctie() Recursieve functies moeten met zorg geschreven worden. Als de programmeur niet goed oplet bij het schrijven van een recursieve functie, blijft de functie zichzelf oneindig aanroepen en loopt het programma vast of crasht het doordat het geheugen van de computer volgelopen is. We noemen dit een overflow. De oplettende lezer had misschien al door dat de code hierboven een voorbeeld is van oneindige recursie. Om te voorkomen dat recursie eindeloos (tot aan een crash) door blijft gaan, moet de programmeur de functie voorzien van een stopconditie. Dit is een punt waarmee het recursieve proces tot stoppen gebracht wordt. Bekijk nu de volgende code: Voorbeeld faculteit(int getal) : int if(getal = 1) return 1 return getal * faculteit(getal * 1) De functie in de code hierboven berekent de faculteit van een getal op een recursieve manier. De stopconditie is in dit geval de booleaanse expressie op de eerste regel van de functie. Onderaan staat de recursieve aanroep waarbij het huidige getal met 1 verminderd als argument meegegeven wordt. De eerste keer dat deze functie 229 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 wordt aangeroepen wordt dus niet direct een resultaat teruggegeven, maar duikt het programma als het ware eerst opnieuw in deze zelfde functie tot aan de stopconditie, waarna alles teruggegeven wordt. Je kunt dit vergelijken met een speelgoedautootje dat je terugdraait tot hij niet meer verder kan, waarna je hem loslaat en hij vervolgens vooruit schiet (en de voorheen afgelegde afstand opnieuw aflegt in de omgekeerde volgorde). Informatie In de wiskunde is de faculteit van een getal (genoteerd als een getal gevolgd door een uitroepteken) een reeks aan vermenigvuldigingen waarbij telkens het getal wordt verminderd met 1 totdat het getal zelf 1 is, bijvoorbeeld: 5! = 5 * 4 * 3 * 2 * 1 3.12 Functioneel programmeren De techniek van recursie is zo universeel en krachtig dat er een hele familie van programmeertalen rond dit concept bestaat. Dit zijn de zogenaamde functionele programmeertalen. In een functionele programmeertaal heeft een variabele, net als in de wiskunde, altijd dezelfde waarde. Je hebt dus geen assignments (toekenningen) zoals x := x + 1 . Variabelen, veelal parameters, krijgen een waarde bij aanroep van de functie waar ze in voor komen. Daarom bestaan deze programma's uit veel aanroepen, vaak recursief, van relatief kleine functies. Deze functies lijken erg op wiskundige functies en zijn daardoor ook goed wiskundig te beschrijven en te analyseren. Programmeren in een functionele programmeertaal is niet zo makkelijk te leren. Abstractievermogen en een wiskundige attitude komen goed van pas. Maar een ervaren programmeur kan er snel en effectief mee programmeren. Voorbeelden van functionele programmeertalen zijn Lisp, Haskell en Clojure. Veel nietfunctionele talen bevatten wel functionele concepten, zoals functies die als parameter kunnen optreden in PHP en C++ en lambda-expressies in Java. 3.13 Vragen en opdrachten 3.13.1 Open vragen 1. Wanneer zal van een else-statement de inhoud worden uitgevoerd? 2. Geef de waarheidstabel van de volgende expressie: x OR (NOT y). 3. a. Noem de Engelse namen van de verschillende typen variabelen die je kent. b. Wat valt je op als je de string met de lijst vergelijkt? 4. a. Welke eigenschappen moet je van een functie definiëren? b. Wat is het verschil tussen een parameter en een argument? 5. Leg in je eigen woorden uit wat syntax betekent. 3.13.2 Meerkeuzevragen 1. Welke van onderstaande beweringen over lijsten is niet waar? a. De eerste waarde van een lijst staat op index 0. b. Een lijst heeft een volgorde waarin de waardes gerangschikt zijn. c. In één lijst kunnen verschillende waardes worden opgeslagen van verschillende typen. d. Een lijst wordt ook wel array genoemd. 2. Bekijk onderstaande voorbeeld: int a = 2 int b = 4 int c = 6 vermenigvuldig(int x, int y) : int return x * y Voorbeeld Voorbeeld van een stukje Haskell: som :: [integer] -> Integer -- som is een functie van een lijst integers naar -- een integer som [] = 0 -- de som van de lege lijst is 0 som {x:xs} = x + som xs -- de som van een niet-lege lijst is het eerste -- element plus de som van de rest Deze functie telt de elementen van een lijst op. 230 c := vermenigvuldig(a, b) Stel dat deze code uitgevoerd zou worden, welke waardes zouden dan correct zijn ná het uitvoeren: a. x is 2, y is 4 en c is 6. b. a is 2, b is 4 en c is 8. c. b is 4, y is 4 en c is 4. d. a is 2, b is 4 en c is 6. 3. Welke van onderstaande beweringen over lussen is niet waar? a. Van een lus wordt de booleaanse expressie herhaaldelijk gecontroleerd. b. Een lus is vergelijkbaar met de iteratie van een PSD. c. Een ander woord voor lus is loop. d. Bij een oneindige lus wordt niet aan de booleaanse expressie voldaan. 231 Module 4 – Softwareontwikkeling Imperatief programmeren Hoofdstuk 3 Hoofdstuk 3 4. Van de volgende booleaanse expressie zijn de operatoren vervangen door cijfers. De waarden voor x en y zijn: x = true, y = false. Welke operatoren kunnen hier staan om de expressie de waarde true te laten zijn? 1 y 2 3 (x 4 y) a. 1 = XOR, 2 = AND, 3 = OR, 4 = XOR b. 1 = OR, 2 = AND, 3 = NOT 4 = AND c. 1 = AND, 2 = AND, 3 = NOT, 4 = AND d. 1 = OR, 2 = XOR, 3 = NOT, 4 = AND 5. Bekijk onderstaande code aandachtig en maak de bewering af zodat deze klopt. if(getal == 1) return 1 return getal * mijnFunctie(getal + 1) Het aanroepen van de functie met mijnFunctie(5) resulteert uiteindelijk in... a. ...het crashen van het programma b. ...een eindeloze herhaling van de functie c. ...het verschijnen van een syntax error d. ...een getal 3.13.3 Korte opdrachten 1. Zoek ten minste vijf syntactische regels voor de programmeertaal Java en beschrijf deze kort in je eigen woorden. 2. a. Schrijf zelf code voor het zetten van koffie. Maak hierbij gebruik van variabelen, ten minste één lus en ten minste één functie. b. Beschrijf vervolgens stapsgewijs de werking van jouw code. Geef hierbij onder andere aan welke variabelen op welk moment welke waarde hebben. 232 Module 4 – Softwareontwikkeling Imperatief programmeren 3.14 Samenvatting ff Imperatief programmeren is het stapsgewijs schrijven van instructies die van boven naar beneden uitgevoerd worden. Kenmerken hierbij zijn: yyif-, then-, else-statemens: gebruikt om voorwaarden aan variabelen te stellen yylussen: herhalingen, denk hierbij aan de while-lus yyfuncties: kleine deelprogramma's om het programma overzichtelijker te maken en herhalende code slechts één keer te hoeven schrijven. ff Een parameter is een variabele die gedefiniëerd is bij een functie. Een argument is de waarde die je meegeeft bij het aanroepen van een functie. ff Er zijn verschillende typen variabelen: yyint: heel getal yydouble of float: kommagetal yychar: een karakterteken yybool of boolean: heeft de waarde true of false yystring: een reeks van karakters. ff Een lijst (of array) is een reeks van waardes van één type variabele. ff Recursie is een zich binnen zichzelf herhalend patroon. ff Een functie is recursief als deze zichzelf aanroept. Wanneer een functie door het ontbreken van een stopconditie zichzelf eindeloos aanroept, crasht het programma door een overflow. ff De syntax van een programmeertaal beschrijft de grammaticaregels waaraan een programmeur zich voor die taal moet houden. 233