Zie Scherp Scherper - Hoofdstuk 6
Na dit hoofdstuk kan je:
while, do while en forbreak en continueMet if konden we vertakken, maar niet teruggaan in ons algoritme.
Hoe vaak die zolang-stap draait weet je niet vooraf. Toch schrijf je ze maar één keer op.
Wil je die zin achteraf aanpassen, dan mag je honderd lijnen bijwerken en vergeet je er gegarandeerd eentje. Elke ronde door een loop heet een iteratie.
Tip
Loops zitten in zowat elk programma dat je dagelijks gebruikt: je game tekent 60 keer per seconde een beeld, je mailprogramma overloopt alle nieuwe berichten, de zelfscankassa blijft scannen tot jij op afsluiten duwt.
Belangrijk
Vergeet je punt 3, dan blijft de conditie eeuwig waar en loopt je programma vast.
Het bakkersalgoritme is een indefinite loop: het aantal gesloten bakkers ken je pas terwijl je aan het wandelen bent.
while: draait 0 of meer keerdo while: draait minstens 1 keerfor: compacter wanneer je weet hoe vaak je moet herhalenforeach: pas in hoofdstuk 12, wanneer we met objecten werken| Looptype | Definite | Indefinite | Oneindig |
|---|---|---|---|
| while / do while | x | x | x |
| for | x |
Tip
Met while kan je meer, en toch val je vaak terug op een for: die is compacter, en heel wat problemen zijn nu eenmaal definite loops.
Zolang de conditie true is, draait het blok. Is ze meteen false, dan draait het blok nooit.
Belangrijk
De conditie zegt wanneer je doorgaat, niet wanneer je stopt. Onthoud dat, want straks bij de invoercontrole loopt het net daar mis.
De loop hiervoor toont 1 tot en met 100. Schrijf de rondes uit:
teller bij de test |
teller < 100 |
In de loop | Op het scherm |
|---|---|---|---|
| 0 | true |
wordt 1 | 1 |
| … | … | … | … |
| 99 | true |
wordt 100 | 100 |
| 100 | false |
loop stopt | niets |
Tip
Zo’n tabel uitschrijven heet een loop tracen. Loop je vast op een loop, teken dan met pen en papier de eerste twee en de laatste twee rondes uit. Dat gaat sneller dan gokken.
Een while mag evengoed een indefinite loop zijn. Geen teller te bespeuren:
Zo’n stopwaarde heet een sentinel. De conditie mag ook complexer, met de logische operators uit hoofdstuk 5:
Belangrijk
getal krijgt een waarde vóór de loop. De test staat bovenaan en draait nog voor de gebruiker iets kon intikken. Die 0 dient enkel om de eerste test te laten slagen.
Bewust oneindig:
Per ongeluk oneindig:
Tip
Zorg dat de variabele uit je conditie effectief verandert, en in de juiste richting. Vastgelopen? Stop met de rode knop in Visual Studio, of pauzeer met Pause/Break.
Je verwacht 1, 3, 6, 10 en krijgt 1, 2, 3, 4. En buiten de accolades bestaat som gewoon niet: The name ‘som’ does not exist in the current context.
Belangrijk
Wil je een som bijhouden over de iteraties heen? Declareer som dan vóór de loop. En wat bevat teller na afloop? Niet 10, maar 11.
Een do while draait minstens één keer: de conditie wordt pas na elke iteratie getest. Op de flowchart is de ruit van plaats gewisseld.
while en de do while naast elkaar.Waarschuwing
Let op de puntkomma na while (conditie);. Vergeten is een klassieke fout (bij een gewone while staat ze er niet).
Welke getallen toont dit aftelprogramma precies?
9 tot en met 0. Dus niet 10 tot en met 1, en ook niet 10 tot en met 0.
Opmerking
Dezelfde val als bij de eerste while: we passen i aan vóór we hem tonen. Wil je 10 tot en met 1 zien, zet dan Console.WriteLine(i); vóór i--;.
Blijven vragen tot de gebruiker a, b of c intikt. Denk in twee stappen.
Stap 1: wanneer is de invoer goed?
Stap 2: draai het om. De conditie zegt wanneer je doorgaat, dus zet er een ! voor:
Dezelfde logica, anders geschreven:
Tip
De Wetten van De Morgan:
!(A && B) is hetzelfde als !A || !B!(A || B) is hetzelfde als !A && !BWelke van beide vormen je schrijft is persoonlijke smaak.
do while?Met een gewone while staat de test vooraan, en dan heeft input nog geen zinnige waarde. Twee omwegen, geen van beide fraai:
Belangrijk
Declareer input vóór de loop: de test achteraan ligt buiten de accolades, dus in een andere scope.
Deze constructie komt zo vaak voor dat er een kortere schrijfwijze voor bestaat:
Dezelfde loop twee keer. Bij de while liggen de drie stukken verspreid over de code, bij de for staan ze naast elkaar in de kop.
int i = 0)i < 11)i += 2)for-flowchart met de code erbij.Tip
Typ for + tweemaal [tab] in Visual Studio voor een kant-en-klare for-loop.
Omdat de test vooraan staat draait een for net als een while 0 of meer keer:
Tip
De teller heet meestal i, van index. Heb je er meerdere nodig, dan volgen j en k. Overloop je effectief rijen, noem hem dan gerust rij.
Opmerking
1 tot en met 9. De finish test i < 10 blijft waar zolang i kleiner is dan 10, dus bij i gelijk aan 10 stopt de loop nog net voor die laatste waarde. Wil Steven de 10 erbij, dan moet er i <= 10 staan (of i < 11). Zo’n vergissing van precies één te veel of te weinig heet een off-by-one-fout. Steven liet de A.I. de grens kiezen en telde zelf niet na.
| Header | i neemt de waarden aan |
Aantal iteraties |
|---|---|---|
for (int i = 0; i < 10; i++) |
0 tot en met 9 | 10 |
for (int i = 1; i < 10; i++) |
1 tot en met 9 | 9 |
for (int i = 1; i <= 10; i++) |
1 tot en met 10 | 10 |
for (int i = 0; i <= 10; i++) |
0 tot en met 10 | 11 |
for (int i = 10; i > 0; i--) |
10 tot en met 1 | 10 |
Start je bij 0 en test je met <, dan staat het aantal iteraties letterlijk in de test. Start je bij 1, dan heb je <= nodig om even vaak te lopen.
Tip
De laatste rij telt af: de teller start op zijn hoogste waarde en de test draait mee om naar >. Met i < 10 zou daar niets gebeuren.
Schrijf je int i = 0 in de setup, dan hoort i bij de loop en verdwijnt hij erna:
Heb je die eindwaarde nadien wél nodig, maak de variabele dan vóór de loop aan en laat de int weg:
Tip
Kom je in die situatie terecht, vraag je dan eerst af of een while niet de duidelijkere keuze is.
De loop krijgt een leeg codeblok: hij draait tien keer en doet tien keer niets. Het blok eronder hoort niet meer bij de loop.
| Constructie | Puntkomma achteraan? |
|---|---|
if (conditie) |
nee, fout |
while (conditie) |
nee, fout |
} while (conditie) |
ja, verplicht |
for (setup; test; update) |
nee, fout |
Dit toont 0, 2, 4, 6 en 8. Wil je stappen van 2, schrijf dan i += 2 in de header en laat de teller in het codeblok met rust.
Waarschuwing
Een for is bedoeld voor een definite loop. Wring je er een sentinel-loop in, dan blijft de update leeg (for (int getal = 0; getal != 666; )) en houd je enkel de omslachtige syntax over. Zulke code hoort in een while.
continue: stop de huidige iteratie, ga naar de volgendebreak: stap meteen volledig uit de loopOlla!? Wat denken we dat we aan het doen zijn?
break en continue zijn de subtiele vrienden van goto. Probeer eerst je loop netjes te stoppen via een goede stopconditie. Met een continue in een while spring je bovendien zo over de verhoging van je teller heen, waarna je programma blijft hangen.
Tip
Wanneer is break wél oké? Bij zoek-en-stop: zodra je gevonden hebt wat je zocht, is verder zoeken zinloos. Gebruik het enkel als het je code echt duidelijker maakt.
Denk in rijen en kolommen: de outer loop overloopt de rijen, de inner de kolommen binnen zo’n rij.
Belangrijk
tellerB = 0 moet elke ronde gereset worden, anders blijft hij op 5 staan en draait de inner loop maar één keer.
Dezelfde uitvoer, minder in de weg. Belangrijker: de reset zit gratis in de setup. Start de outer loop een nieuwe ronde, dan komt de volledige inner loop opnieuw aan de beurt, int j = 1 incluis.
Tip
Daarom worden geneste loops in de praktijk bijna altijd met een for geschreven: de vergeten reset kan je hier niet maken.
i wordt 1 (één keer)i <= 3j wordt 1 (elke ronde opnieuw)j <= 5j++, terug naar 4i++, terug naar 2Stappen 4, 5 en 6 draaien vijf keer voor er ook maar één keer aan stap 7 wordt toegekomen.
Console.Write springt niet naar de volgende lijn, \t is een tab.
Belangrijk
Die laatste Console.WriteLine(); staat binnen de outer loop, buiten de inner. Zet je ze in de inner loop, dan krijg je 25 lijnen met één getal. Zet je ze buiten de outer loop, dan komt alles op één lange lijn.
Outer 10 keer, inner telkens 5 keer: 10 x 5 = 50 keer “Hallo”.
Waarschuwing
Elke extra laag vermenigvuldigt het werk. Twee loops van elk 1000 rondes leveren een miljoen uitvoeringen op, en met een derde erbij zit je aan een miljard.
Samen: 3 x 5 x 7 = 105 keer.
Waarschuwing
De test van de inner loop gebruikt hier rij. De inner loop draait dus 1, 2, 3, 4 en 5 keer: samen 15 sterretjes, en niet 5 x 5 = 25. Zodra de teller van de outer loop in de header van de inner opduikt, mag je niet meer zomaar vermenigvuldigen.
Hoe vaak verschijnt “Hallo”?
Slechts 10 keer, niet 40! Na de eerste ronde staat teller al op 10 en draait de while nooit meer.
Belangrijk
Een for reset zijn teller gratis bij de setup; een while niet. Zet teller = 0; in de for-body om wel 40 keer te draaien.
break haalt je enkel uit de huidige loop. Uit beide springen doe je met een booleaanse vlag in de condities:
Zodra gevonden op true staat falen beide tests. De reden om te stoppen staat in de conditie zelf, waar een lezer ze verwacht.
Welke loop kiezen we?
Blijf wel kritisch over de opgave: de vereisten bepalen uiteindelijk je keuze.
Elke game draait op een grote loop die blijft lopen tot het spel gedaan is:
Input, update, render, wachten, en opnieuw. Tientallen keren per seconde.
int x = 3, y = 3; // positie
int xv = 1, yv = 1; // snelheid (velocity)
while (true)
{
x += xv;
y += yv;
if (x <= 0 || x >= Console.WindowWidth - 1) xv = -xv; // bots tegen de zijkant
if (y <= 0 || y >= Console.WindowHeight - 1) yv = -yv; // bots tegen boven/onder
Console.Clear();
Console.SetCursorPosition(x, y);
Console.Write("O");
System.Threading.Thread.Sleep(50);
}Tip
Met enkel een lus, Console.SetCursorPosition en Thread.Sleep maak je al animatie. Console saai? Valt dus reuze mee.
Een trein rijdt langs stations (elk 5 km uit elkaar). Aan elk station stappen passagiers in en uit (de gebruiker geeft het in). Hou bij:
Combineer alles uit dit hoofdstuk:
while of for over de stationsfor voor laadbeurten van goederenRandom voor onverwachte gebeurtenissen (sneeuw, bandieten, …)Tip
Begin met een flowchart en bouw het stap voor stap uit: eerst de stations, dan de uitbreidingen.
while (0 of meer keer) en do while (minstens 1 keer; let op de ;)for is compact wanneer je een teller bijhoudt: setup, finish test, updatebreak: schrijf liever een deftige stopconditieTip
Volgende halte: methoden, om je code op te delen in herbruikbare blokken.
Kennisclips
Dezelfde concepten uit dit hoofdstuk, maar dan in andere talen. Handig om later code in Python of JavaScript te leren lezen.
De do while ken je terug in C, C++, Java en JavaScript. Python heeft hem niet. Iets dat minstens één keer draait, bootst men er na met while True + break:
In Python ben je dus vaak verplicht om break te gebruiken waar C# een nette stopconditie achteraan de do while toelaat.
De drie-delige for komt uit C en is bijna letterlijk overgenomen door C++, Java en JavaScript:
In C-code duikt ook for(;;) op: alle drie de delen mogen leeg blijven, wat een oneindige loop oplevert. Dat mag in C# evengoed, al schrijft zowat iedereen daar while(true).
Python kent geen teller-met-conditie: je loopt er altijd over iets. Tellen doe je met range:
Losse setup, conditie en update zijn er dus niet, die zitten verstopt in range. Dat werkt enkel als je vooraf weet over welke reeks je loopt.
Een handige samenvattende poster (Opgelet: gemaakt m.b.v. ChatGPT Image Gen 2).