Stelt u zich even voor dat u een programma moet schrijven dat 100 vragen bevat. Die vragen moeten één voor één op het scherm verschijnen, waarna de gebruiker moet kiezen uit een lijst van mogelijke antwoorden. Een enquete of een multiple choice examen, bijvoorbeeld. Nu kunt u natuurlijk de vragen bijhouden in tekstvariabelen (strings, meer daarover later) met als naam: vraag1, vraag2, … , vraag100. En de antwoorden van de gebruikers kunt u opslaan in antwoord1, antwoord2, antwoord3, … tot en met antwoord100. Dat zijn dus 100 cout en 100 cin statements die u moet schrijven, en dan hebben we ‘t nog niet over de verwerking gehad. Als het een examen is, moet u 100 if statements schrijven, om te zien of de antwoorden juist waren, bij de verwerking van de enquete moet u misschien nog veel meer gaan doen met die 100 variabelen. Een onbegonnen zaak, natuurlijk. Gelukkig, C++ heeft meerdere oplossingen voor een dergelijk probleem. Eén van de best bekende (die in alle programmeertalen terug te vinden zijn) zijn arrays. Een array is gewoon een reeks van gelijkaardige variabelen (allemaal int of double, bijvoorbeeld).
Omdat we al met gemiddelde werkten, zullen we een testje simuleren uit de statistiek. We gaan met een dobbelsteen gooien. Normaal gezien is de kans op een bepaald cijfer bij een dobbelsteen 1/6. Als we 60 keer gooien zou je verwachten van 10 enen, 10 tweeën, enzovoort te hebben. Hoe meer keer je gooit, hoe dichter we bij die theoretische 16.666… % voor elk cijfer zouden moeten komen. Om de dobbelsteen te simuleren, maken we gebruik van een randomgenerator. Randomgeneratoren worden vaak gebruikt in programma’s, zeker in spelletjes, maar ook in meer serieuze simulaties.
Een voorbeeld van het programma in actie:
De code:
Op lijn 2 doen we een include van time.h. Dat hebben we nodig, omdat we straks een tijdsfunctie gaan gebruiken, en die is daar te vinden.
Lijn tien geeft de maximum waarde die een random getal kan aannemen op uw systeem weer. Dat kan al eens verschillen, en daarom wordt deze waarde vastgelegd voor u, in de constante (bemerk de hoofdletters) RAND_MAX.
Lijn 12 bevat de seed instructie. Om iedere keer dat we het programma draaien een andere “random” waarde te krijgen, moeten we de randomfunctie een startgetal of “seed” meegeven. Dat gebeurt hier met de functie srand. Als seed gebruiken we hier gewoon de huidige tijd van de computerklok. Die is namelijk iedere keer anders, wat ons ook garandeerd dat we altijd een andere willekeurige reeks getallen krijgen. Zet deze instructie in commentaar, en u zult merken dat u steeds dezelfde randomgetallen krijgt, bij iedere run.
Terzijde, een computer kan niet echt randomgetallen genereren, wel pseudorandomgetallen. Er zit een algoritme achter, dus echt willekeurig kan per definitie niet. Vandaar ook dat u zonder seed steeds dezelfde reeks krijgt. Er is nogal wat geschreven over het genereren van randomgetalllen met computers.
Lijnen 18 en 19 definiëren de constante AANTAL_GOOIEN. Zet één van de 2 in commentaar om verschillende aantallen te proberen (hoe groter, hoe dichter bij de theoretische uitkomsten). Ik gebruik een long int als type. Ieder type (int, double, etc…) heeft zo zijn beperkingen. Ook dat kan weer schelen van computer tot computer, maar in ANSI C++ (een standaard) heeft een int een gegarandeerde maximumgrootte van 2^16 = 65536. Echter, de eerste bit (bij signed integer, tenminste) is een tekenbit, zodat we in een integer een waarde kwijt kunnen tussen ongeveer -32000 en +32000 (de helft dus van ‘t voorgaande). De meeste compilers staan grotere getallen toe in het int type, maar daar is geen garantie op. Long integer is 2^32 groot, gegarandeerd. Veel groter dus, en ik speel hier op veilig, want ik zet de constante op 50000000, wat normaal te groot is voor een gewoon integer type. Dus gebruiken we long int. Maak u maar geen zorgen als u niet alles hiervan snapt. Onthou gewoon dat als je een te groot getal in een int steekt, de computer daar gewoon een stuk van kwijtspeelt, het potje loopt als het ware over, en dat dat rare gevolgen kan hebben. Zo is er ooit een raket neergestort vanwege een dergelijke programmeerfout. Iets om in het achterhoofd te houden als je voor de ESA of NASA ofzo gaat programmeren.
Lijn 21 bevat de definitie van een array. In dit geval dus een array van type long int (we spelen andermaal op veilig), die zes plaatsen bevat. De instructie is: long in waarde[6] = {0}. De 6 slaat dus op het aantal long integers, en we initialiseren alle 6 de waarden op 0. Er zijn verschillende manieren om arrays te initialiseren, we zullen er nog wel een aantal tegenkomen in de loop van de cursus. Een eigenaardigheidje is dat C++ (en zowat alle andere programmeertalen) begint te tellen vanaf 0. We hebben dus 6 variabelen met de naam waarde en elk een nummer, waarvan de eerste waarde[0] en de laatste waarde[5] heet. Dit is iets wat bij veel mensen in het begin voor verwarring zorgt.
Lijn 23 definieert de for lus, en dus meteen hoeveel keer we met de dobbelsteen gaan werpen (of dit althans gaan simuleren). Dit is afhankelijk van de waarde die we in de constante AANTAL_GOOIEN gedefinieerd hebben.
Op lijn 25 gebruiken we de functie rand, die niets anders doet dan een randomgetal in de variabele randomwaarde steken.
Lijn 26 bevat een nieuwe operator, namelijk de % of modulo operator. Modulo geeft in feite niets anders dan de rest bij deling terug. Zo is 7%2 = 1 want 7/2 is 3 met rest 1. Hier delen we modulo 6, de rest kan dus nooit groter zijn dan 5. Als het getal deelbaar is door 6, is de ret natuurlijk 0. We krijgen dus een waarde die van 0 tot 5 kan lopen, maar omdat dobbelstenen getallen van 1 tot 6 produceren, tellen we daar eentje bij op, en steken het resultaat in de variabele dobbelsteen.
Op de volgende lijn zien we dan waarde[dobbelsteen – 1] = waarde[dobbelsteen – 1] + 1 staan. We willen in de array waarde namelijk bijhouden hoeveel keer we 1, 2, 3, 4, 5 of 6 gegooid hebben. In de eerste waarde in de rij – waarde[0] dus (we beginnen vanaf 0 bij arrays) – houden we het aantal gegooide enen bij, in de tweede waarde – waarde[1] – het aantal tweeën, enzoverder… Vandaar dus dat we dobbelsteen – 1 schrijven. Als we een 1 gooien, staat daar dus in feite: waarde[1-1] = waarde[1-1] +1. We tellen dus 1 bij, bij de waarde die in waarde[0] zat.
Nadat we AANTAL_GOOIEN keer gegooid hebben gaan we verder met het programma.
Op lijn 39 definiëren we een for lus waarmee we in eerste isntantie kijken hoeveel keer we ieder oog van de dobbelsteen gegooid hebben. Dat doen we met de cout instructie die de waardes in waarde[0 tot 5] weergeeft. Moesten we een variabele waarde hebben die 1000 getallen bevat, dan zouden we hier niet meer code moeten schrijven, enkel de for lus zou moeten aangepast worden: inplaats van i < 6 zouden we moeten schrijven i < 1000. Een heel verschil met 1000 cout instructies moesten we 1000 aparte variabelen gebruikt hebben in plaats van een array.
Op lijn 45 berekenen we het totaal aantal ogen die er gegooid zijn (in feite: waarde[0] * 1 + waarde[1] * 2 + … + waarde [5] * 6. Dit gebeurd nog steeds binnen de lus. Moesten we aparte variabelen gebruiken, dan dienden we hier 6 getallen op te tellen. In het geval van 1000 verschillende variabelen, zou dat een optelling worden van een paar bladzijden lang.
Ik hoop dat hiermee het belang en het gemak van arrays enigszins aangetoond is.
Nog een belangrijk detail. C++ controleert niet of u de juiste grenzen gebruikt voor uw array. In ons geval zijn de grenzen 0 en 5. Maar ik kon ook iets geschreven hebben als waarde[15] = 100. C++ zou daar niets over zeggen, maar het zou wel nefast zijn voor de uitvoer van mijn programma. Dat komt omdat ik bij de definitie 6 geheugenplaatsen gereserveerd had. Na die gereserveerde plaats komt andere data, of zelfs programmacode. Met de foutieve instructie aarde[15] = 100 schrijf ik in feite de waarde 100 in een stuk geheugen waar ik moet uitblijven. Dat kan erg rare gevolgen hebben, en het is vrijwel zeker dat het programma gaat crashen. Dit is definitief iets waar je zeker altijd moet op letten, dat je nooit of te nimmer de grenzen (boundaries) van een array overschrijdt. Onthou het!
U kunt de code hier downloaden, deze is voorzien van extra commentaar.
Vragen kunnen gesteld worden op het forum – op het C++ board.
Opdracht
- Maak een nieuw project aan, met daarin een file “main”. Noem het “project_1.8”.
- Typ het programma over en compileer.
- Pruts er wat mee. 🙂