Herhalingen (loops) creëer je wanneer bepaalde code een aantal keer moet herhaald worden. Hoe vaak de herhaling moet duren is afhankelijk van de conditie die je hebt bepaald.
In het vorige hoofdstuk leerden we hoe we met behulp van beslissingen onze code konden branchen, aftakken zodat andere code werd uitgevoerd afhankelijk van de staat van bepaalde variabelen of invoer van de gebruiker. Wat we nog niet konden was terug naar boven vertakken. Soms willen we dat een heel stuk code 2 of meerdere keren moet uitgevoerd worden tot aan een bepaalde conditie wordt voldaan. "Voer volgende code uit tot dat de gebruiker 666 invoert."
Door herhalende code met loops te schrijven maken we onze code korter en bijgevolg ook minder foutgevoelig en beter onderhoudbaar.
Definite of counted loop: een loop waar het aantal iteraties vooraf van gekend is. (bv. alle getallen van 0 tot en met 100 tonen)
Indefinite of sentinel loop: een loop waarvan op voorhand niet kan gezegd worden hoe vaak deze zal uitgevoerd worden. Input van de gebruiker of een interne test zal bepalen wanneer de loop stopt (bv. "Voer getallen in, voer -1 in om te stoppen" of "Bereken de grootste gemene deler")
Oneindige loop: een loop die nooit stopt. Soms gewenst (bv. de game loop) of, vaker, een bug.
Loops in C#
Er zijn 3 manieren om zogenaamde loops te maken in C#:
while: zal 0 of meerdere keren uitgevoerd worden
do while: zal minimaal 1 keer uitgevoerd worden
for
Voorts zullen we ook een speciale loop variant zien in het volgende semester wanneer we arrays en objecten leren kennen:
NEVER EVAH USE GOTO
Het moet hier alvast even uit m'n systeem. goto is weliswaar een officieel C# keyword, toch zal je het in deze cursus nooit zien terugkomen in code. Je kan alle problemen in je algoritmes oplossen zonder ooit goto nodig te hebben. Voel je toch de drang: don't! Simpelweg, don't. Het is het niet waard. Geloof me. NEVER USE GOTO.
.
: een alternatieve iets compactere manier om loops te beschrijven
Een veelvoorkomende manier van while-loops gebruiken is waarbij je een bepaalde teller bijhoudt die je telkens met een bepaalde waarde verhoogt. Wanneer de teller een bepaalde waarde bereikt moet de loop afgesloten worden.
Bijvoorbeeld volgende code om alle even getallen van 0 tot 10 te tonen:
Met een for-loop kunnen we deze veel voorkomende code-constructie verkort schrijven.
For syntax
De syntax van een for-loop is de volgende:
setup: In het setup gedeelte zetten we de "wachter-variabele" op de begin waarde. De wachter-variabele is de variabele die we tijdens de loop in het oog zullen houden en die zal bepalen hoe vaak de loop moet uitgevoerd worden.
finish test: Hier plaatsen we een booleaanse expressie die de wachter-variabele uit de setup gebruikt om te testen of de loop-code moet uitgevoerd worden.
update: Hier plaatsen we wat er moet gebeuren telkens de loop z'n codeblock heeft uitgevoerd. Meestal zullen we hier de wachter-variabele verhogen of verlagen.
Gebruiken we deze kennis nu, dan kunnen we de eerder vermelde code om de even getallen van 0 tot en met 10 tonen als volgt:
Voor de setup-variabele kiest men meestal i, maar dat is niet noodzakelijk. In de setup wordt dus een variabele op een start-waarde gezet. De finish test zal aan de start van iedere loop kijken of de finish test nog waar is, indien dat het geval is dan wordt een nieuwe loop gestart en wordt i met een bepaalde waarde, zoals in update aangegeven, verhoogd.
Lees zeker want er zijn nog enkele subtiliteiten in for-loops die we hier niet behandelen.
for-tab-tab
Als je in Visual Studio for typt en dan tweemaal op [tab] duwt krijg je kant en klare for-loop code.
Break
Je kan loops (alle types) altijd vroegtijdig stopzetten door het break keyword. Het gebruik hiervan ligt soms in de schemerzone van misbruik. Probeer dus eerst je probleem anders op te lossen voor je break begint te gebruiken.
Om iemand op StackOverflow te quoten:
When used at the start of a block, as first checks made, they act like preconditions, so it's good. When used in the middle of the block, with some code around, they act like hidden traps, so it's bad."
Lees meer over het gebruik van break .
Kennisclip
int k = 0;
while(k<11)
{
Console.WriteLine(k);
k = k + 2;
}
for (setup; finish test; update)
{
// C# die zal uitgevoerd worden zolang de finish test true geeft
}
for (int i = 0; i < 11; i += 2)
{
Console.WriteLine(i);
}
Nesting
Nested loops
Wanneer we 1 of meerdere loops in een andere loop plaatsen dan spreken we over geneste loops. Geneste loops komen vaak voor, maar zijn wel een ras apart wanneer je deze zaken wilt debuggen en correct schrijven.
We spreken steeds over de outer loop als de omhullende of "grootste" loop. Waarbij de binnenste loops de inner loop(s) zijn.
Volgende code toont bijvoorbeeld 2 loops die genest werden:
De uitvoer hiervan zal als volgt zijn:
Begrijp je hoe we aan deze uitvoer komen? (tip: analyseer de inner en outer loop apart)
Geneste loops tellen
Om te tellen hoe vaak de 'inner' code zal uitgevoerd worden dien je te weten hoe vaak iedere loop afzonderlijk wordt uitgevoerd. Vervolgens vermenenigvuldig je al deze getallen met elkaar.
Een voorbeeld: Hoe vaak zal het woord Hallo op het scherm verschijnen bij volgende code?
De outer loop zal 10 maal uitgevoerd worden (i zal de waarden 0 tot en met 9 krijgen). De inner loop zal 5 maal (j zal de waarden 0 tot en met 4 krijgen) uitgevoerd worden. In totaal zal dus 50 maal Hallo op het scherm verschijnen (5x10).
Bekijk maak de oefeningen 8 tot en met 13 van hoofdstuk 4 in volgende pdf
Ter info: Dit document staat ook in de lijst onderaan bij de nuttige extra's .
De oefeningen
Indien niet expliciet vermeld mag je kiezen met wat voor loop (for, while, do while) je het probleem zal oplossen.
Tafels van vermenigvuldigen
Gebruik de kracht van loops om pijlsnel alle tafels van 1 tot en met 10 van vermenigvuldigen op het scherm te tonen (dus van 1x1 tot 10x10 en alles daartussen)
RNA Transscriptie
DNA heeft steeds een RNA-complement (DNA is het gevolg van RNA transscriptie). Schrijf een programma dat een ingevoerde DNA-string omzet naar een RNA-string. De gebruiker voert steeds 1 DNA-nucleotide in per keer en duwt op enter, de RNA string wordt steeds groter. De omzetting is als volgt:
G wordt C
C wordt G
T wordt A
Als de gebruiker dus ACGTGGTCTTAA heeft ingevoerd moet het resultaat: UGCACCAGAAUU zijn.
Ga er van uit dat de gebruiker letter per letter invoert (telkens dus enter na een letter) en je de omgezette string doet groeien (m.b.v +=).
PRO Armstrong nummer
Een getal is een narcistisch getal of armstronggetal als het de som is van zijn eigen cijfers elk tot de macht verheven van het aantal cijfers.
9 is een Armstrong nummer, want 9 = 9^1 = 9
10 is geen Armstrong nummer, want 10 != 1^2 + 0^2 = 1
Je zou deze oefening kunnen oplossen door het ingevoerde getal als string op te splitsen in individuele char's. Maar ik raad je aan om de "wiskunde" weg te volgen zodat je terdege leert met loops te wiskunde.*
Tip 1: Stel dat je het getal 4560 hebt:
Eerst deel je 4563 door 1000. Dit geeft 4.
We trekken 4x1000 van 4563 af. Dit geeft 563.
Deel 563 door 100. Dit geeft 5.
Tip 2: Je kan aan een string vragen hoe groot deze is als volgt:
Je kan dan nu met Math.Pow(10,lengte-1) berekenen vanaf welke exponent van 10 we moeten beginnen werken.
Euler project
Maak volgende opdracht van :
Indien we alle natuurlijke getallen van 0 tot en met 10 oplijsten die een meervoud van 3 of 5 zijn, dan krijgen we de getallen 3,5,6,9 en 10. De som van deze 4 getallen is 33. Maak nu een programma dat de som van alle veelvouden van 3 of 5 weergeeft onder van 0 tot 1000 (dit zou 234168) moeten geven.
Tip: module is je grote held hier. Een getal is een veelvoud van x indien getal%x 0 als resultaat geeft.
PRO: For doordenker
Schrijf een programma dat de volgende output geeft , gegeven dat de gebruiker een maximum waarde invoert , dus als hij 4 ingeeft dan zal de driehoek maximum 4 breed worden. Gebruik enkel forloops!
PRO: For doordenker extra
Schrijf een programma dat de volgende output geeft (zie ook WhileDoordenker van vorige labo), gegeven dat de gebruiker een maximum waarde invoert die opgeeft uit hoeveel lijnen de boom bestaat. Maak enkel gebruik van for-loops.
int lengte= myInputGetal.Length; //veronderstellend dat myInputGetal van het type string is
*
**
***
****
***
**
*
*
***
*****
*******
*********
***********
Programma flow analyse
Programma flow analyse
Door een flowchart op te stellen is het vaak veel eenvoudiger om een programma ofwel te analyseren (van code naar idee) ofwel om een programma te schrijven (van idee naar code).
Een flowchart (letterlijk: stroomkaart) of stroomdiagram is een schematische beschrijving van een proces. Met een flowchart kan je vaak ingewikkelde stukken code visualiseren waardoor het geheel plots niet meer zo ingewikkeld is.
Een flowchart bestaat uit een aantal elementen:
Pijl: een pijl geeft aan naar welk volgende blok wordt gegaan. Indien boven de pijl een bepaalde waarde staat wil dit zeggen dat deze pijl enkel wordt gevolgd als de uitkomst van het vorige blok de getoonde waarde geeft.
Start en einde: aangegeven met een cirkel met daarin de woorden "Start" of "Einde"
Verwerk-stap: een statement zoals "Voeg 1 toe aan X" wordt in een rechthoek geplaatst. Alle code die geen invoer nodig heeft zet je in een rechthoek.
Flow-elementen
We tonen nu kort de verschillende program flow elementen en hoe ze in een flowchart voorkomen.
If-element
If-else element
While-element
Do while-element
Merk op dat bij if en if-else de flow niet naar een eerder punt in de code gaat. Dit is dus de manier om een while/do while te herkennen: er wordt naar een eerder punt in de code gegaan, een punt waar we reeds geweest waren
For-element
Voorbeeld flowchart
Door de eerder beschreven elementen nu samen te voegen kunnen we een programma als een flowchart voorstellen. Stel dat we een programma "Faculteit" maken. Hierin is het de bedoeling om de faculteit van een gegeven getal N dat door de gebruiker wordt ingevoerd, te berekenen (bijvoorbeeld N=5 geeft dan 5! = 5x4x3x2x1 = 120 ). De finale flowchart ziet er als volgt uit:
Zoals verteld kunnen we een flowchart in beide richtingen gebruiken. We hebben de flowchart hiervoor gemaakt, gebaseerd op onze oplossing van het vorige labo. Maar stel dat je deze flowchart krijgt, dan kan je dus ook deze chart rechtstreeks omzetten in C#.
Code 2 flow
Via de website en app "Code2flow" () kan je heel eenvoudig een flowchart genereren van pseudocode.
Oefeningen deel 2
Een extra grote hoop oefeningen om je loops te drillen (). De oefeningen zijn gerangschikt naar moeilijkheid, je moet deze allemaal met loops oplossen! Hoe ver geraak je?
Probeer niet alle oefeningen met hetzelfde type loop te doen, wissel tussen while, do...while en for.
Indien er sprake is vannin de opgave dan is dit een getal dat je eerst aan de gebruiker moet vragen.
Oefeningen deel 3
Oefening: H5-boekhouder
Leerdoelen
een taak herhaaldelijk uitvoeren met een lus
Functionele analyse
Maak een 'boekhoud-programma': de gebruiker kan continu positieve en negatieve getallen invoeren. Dit programma houdt volgende zaken bij:
de totale balans
de som van de positieve getallen
de som van de negatieve getallen
het gemiddelde
Technische analyse
Voor de eerste drie zaken kom je toe met een variabele. Voor de laatste is dit lastiger, omdat elk nieuw getal een kleiner effect heeft om het gemiddelde dan het vorige. Je houdt beter een teller bij met het aantal ingevoerde getallen. Dan is het gemiddelde de totale balans gedeeld door het aantal ingevoerde getallen.
UI
console applicatie
voorbeeldinteractie(s)
(Dit programma kan blijven verder lopen zo lang je wil.)
Oefening: H5-schaar-steen-papier
Leerdoelen
een taak herhaaldelijk uitvoeren met een lus
Functionele analyse
Maak een applicatie waarbij de gebruiker steen-schaar-papier met de computer kan spelen. De gebruiker kiest telkens steen, schaar of papier en drukt op enter. Vervolgens kiest de computer willekeurig steen, schaar of papier.
Vervolgens krijgt de winnaar 1 punt:
Steen wint van schaar, verliest van papier
Papier wint van steen, verliest van schaar
Schaar wint van papier, verliest van steen
Indien beide hetzelfde hebben wint niemand een punt.
De eerste (pc of gebruiker) die 10 punten haalt wint.
Technische analyse
Genereer een willekeurig getal tussen 1 en 3 om de computer te laten kiezen.
Teken een flowchart!
UI
console applicatie
voorbeeldinteractie(s)
(Helemaal op het einde)
of
Geef een getal
> 7
De balans is 7
De som van de positieve getallen is 7
De som van de negatieve getallen is 0
Het gemiddelde is 7
Geef een getal
> -3
De balans is 4
De som van de positieve getallen is 7
De som van de negatieve getallen is -3
Het gemiddelde is 2
Geef een getal
> 11
De balans is 15
De som van de positieve getallen is 18
De som van de negatieve getallen is -3
Het gemiddelde is 5
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 2
De computer kiest steen!
Niemand wint deze ronde!
Jij hebt 0 punten, de computer heeft 0 punten.
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 1
De computer kiest papier!
Jij wint deze ronde!
Jij hebt 1 punt, de computer heeft 0 punten.
Maak een keuze:
1 voor schaar
2 voor steen
3 voor papier
> 1
De computer kiest steen!
De computer wint deze ronde!
Jij hebt 1 punt, de computer heeft 1 punt.
Jij hebt 10 punten, de computer heeft 8 punten.
Jij bent gewonnen!
Jij hebt 8 punten, de computer heeft 10 punten.
De computer is gewonnen!
Input/output: Een parallellogram gebruik je om in-of uitvoer van de gebruiker of scherm te tonen. Bv "Verkrijg X van gebruiker" of "Toon volgende zin op het scherm".
Condities en beslissingen: Een ruit wordt gebruikt wanneer een beslissing moet genomen worden. De condities van if en while-loops zet je dus in een ruit. De pijlen die eruit volgen geven aan welke pijl moet gevolgd worden gegeven een bepaalde waarde van de conditie.
Opgelet: de oplossing van dit soort oefeningen vindt je overal. Weersta hier aan, en probeer ZELF de oplossing te vinden. Dat is de enige manier om dit te leren.
Toon alle natuurlijke getallen van 1 tot n.
Toon alle natuurlijke getallen van n tot 1.
Toon alle even getallen tussen 1 en 100.
Toon alle oneven getallen tussen 1 en 100.
Toon de som van alle getallen van 1 tot n (dus 1+2+3+4+...+n).
Toon de som van alle even getallen van 1 tot n.
Toon de som van alle oneven getallen van 1 tot n.
Schrijf een programma dat het aantal digits in een getal telt (het getal 12348 heeft bijvoorbeeld 5 digits).
(PRO) Schrijf een programma dat een ingevoerd getal als tekst uitschrijft. Als de gebruiker dus 123 invoert zal de uitvoer zijn: honderd drie en twintig.
Schrijf een programma dat alle ascii karakters en hun waarde toont van 10 tot n (tip: char c = Convert.ToChar(65); zal hoofdletter A tonen)
Toon het alfabet van a tot z.
Schrijf een programma dat de macht van een getal toont. De gebruiker voor eerst het getal in, gevolgd door de macht (bv 2 en 4 zal als resultaat 16 geven (2 tot de 4e)).
Schrijf een programma een getal n ontbindt in . Factoren zijn de getallen waardoor je n kan delen zonder rest (van bijvoorbeeld het getal 100 zijn de factoren 1,2,4,5,10,20,25,50,100 ).
Toon de reeks van tot n termen.
Oefening: H5-priem-checker
Leerdoelen
lussen gebruiken om een resultaat op te bouwen
Functionele analyse
Je krijgt een getal van de gebruiker. Je moet nagaan of dit een priemgetal is, d.w.z. of het precies 2 gehele delers heeft.
Technische analyse
Elk geheel getal vanaf 2 heeft minstens 2 gehele delers: 1 en zichzelf. Als dat de enige delers van het gegeven getal zijn, is het priem. Je kan dus nagaan of een getal een priemgetal is door alle getallen vanaf 1 tot het getal zelf te overlopen en na te gaan of deze delers zijn van het getal. (Eigenlijk volstaat het minder getallen te checken maar daar draait het hier niet om.)
UI
console applicatie
voorbeeldinteractie(s)
Oefening: H5-priem-generator
Leerdoelen
geneste lussen gebruiken om een meerdere resultaten te laten zien
Functionele analyse
Je krijgt een getal van de gebruiker. Je moet alle priemgetallen kleiner of gelijk aan dit getal laten zien.
Technische analyse
Je kan het idee uit de vorige oefening herbruiken, maar nu zijn de getallen die je controleert niet afgeleverd door de gebruiker. Je moet ze zelf genereren in een for-lus. Als je in de vorige oefening een for-lus hebt gebruikt, zal je dus twee for-lussen moeten nesten.
is zal de code binnen de accolades uitgevoerd worden. Indien dus de conditie reeds vanaf het begin
false
is dan zal de code binnen de
while
-loop niet worden uitgevoerd.
Telkens wanneer het programma aan het einde van het while codeblock komt springt het terug naar de conditie bovenaan en zal de test wederom uitvoeren. Is deze weer true dan wordt de code weer uitgevoerd. Van zodra de test false is zal de code voorbij het codeblock springen en na het while codeblok doorgaan.
Het diagramma is duidelijk:
Een voorbeeld van een eenvoudige while loop:
Zolang myCount kleiner is dan 100 (myCount < 100) zal myCount met 1 verhoogd worden en zal de huidige waarde van myCount getoond worden. We krijgen met dit programma dus alle getallen van 1 tot en met 100 op het scherm onder elkaar te zien.
Daar de test gebeurt aan het begin van de loop wil dit zeggen dat het getal 100 nog wel getoond zal worden. Begrijp je waarom? Test dit zelf!
Do while
In tegenstelling tot een while loop, zal een do-while loop sowieso minstens 1 keer uitgevoerd worden. Ongeacht de opgegeven conditie zal de do-while loop zijn code 1 keer uitvoeren. We herhalen deze zin uitdrukkelijk 2x zodat het verschil tussen beide type loops duidelijk blijft.
Vergelijk volgende diagramma van de do while:
met die hierboven van de while.
De syntax van een do-while is eveneens verraderlijk eenvoudig:
Uiteraard mag de conditie waaraan een loop moet voldoen complexer zijn door middel van de relationele operatoren.
Volgende while bijvoorbeeld zal uitgevoerd worden zolang teller groter is dan 5 en de variabele naam van het type string niet gelijk is aan "tim":
Oneindige loops
Indien de loop-conditie nooit false wordt dan heb je een oneindige loop gemaakt. Soms is dit gewenst gedrag (bijvoorbeeld bij de gameloop) soms is dit een bug en zal je dit moeten debuggen.
Volgende twee voorbeelden tonen dit:
Een bewust oneindige loop:
Een bug die een oneindige loop veroorzaakt:
Probeer er altijd zeker van te zijn dat de variabele(n) die je gebruikt in je test-conditie ook in de loop aangepast worden. Als deze in de loop constant blijft dan zal ook de test-conditie dezelfde blijven en heb je dus een oneindige loop gemaakt.
Scope van variabelen in loops
Let er op dat de scope van variabelen bij loops zeer belangrijk is. Indien je een variabelen binnen de loop definieert dan zal deze steeds terug "gereset" worden wanneer de volgende cyclus van de loop start. Volgende code toont bijvoorbeeld foutief hoe je de som van de eerste 10 getallen (1+2+3+...+10) zou maken:
De correcte manier om dit op te lossen is te beseffen dat de variabele som enkel binnen de accolades van de while-loop gekend is. Op de koop toe wordt deze steeds terug op 0 gezet en er kan dus geen som van alle teller-waarden bijgehouden worden:
Foute input van gebruiker met loops verwerken
Dankzij loops kunnen we nu ook eenvoudiger omgaan met foutieve input van de gebruiker. Stel dat we volgende vraag hebben:
Met een loop kunnen we nu deze vragen blijven stellen tot de gebruiker een geldige input geeft:
Zolang (while) de gebruiker niet "a" OF "b" OF "c" invoert zal de loop zichzelf blijven herhalen.
Merk op dat we de variabele string input VOOR de do while moeten aanmaken. Zouden we die IN de loop pas aanmaken dan zou de variabele niet als test kunnen gebruikt worden aan het einde van de loop.
Je ziet dat het stuk } while(input...); achteraan buiten de accolades van de loop ligt en dus een andere scope heeft).