Programmeren VBA 2. Colofon: Uitgave 1.1 : M.M. Witkam, januari 2003 Nummer : 1022 Auteur : drs. M.M. Witkam Profieldeel : Profiel : Wiskunde Prijs



Vergelijkbare documenten
Programmeren VBA 1. Colofon: Uitgave 1.1 : M.M. Witkam, juni 2002 Nummer : 1021 Auteur : drs. M.M. Witkam Profieldeel : Profiel : Wiskunde Prijs

II. ZELFGEDEFINIEERDE FUNCTIES

6.2 VBA Syntax. Inleiding Visual Basic

Hoofdstuk 7: Werken met arrays

6.3 VBA Syntax Instructie. Wij gaan de Visual Basic Editor opnieuw openen, om de instructie die wij zojuist getypt hebben, nader te bekijken.

VAN HET PROGRAMMEREN. Inleiding

Variabelen en statements in ActionScript

1.3 Rekenen met pijlen

VAN HET PROGRAMMEREN. Inleiding. Het spiraalmodel. De programmeertaal. vervolgens de berekening van het totale bedrag, incl. BTW:

Constanten. Variabelen. Expressies. Variabelen. Constanten. Voorbeeld : varid.py. een symbolische naam voor een object.

Info-books. Toegepaste Informatica. Deel 20 : Algoritmen en programmeren in Access en Excel (Basis) AL20. Jos Gils Erik Goossens

1 Delers 1. 3 Grootste gemene deler en kleinste gemene veelvoud 12

8.1 Herleiden [1] Herleiden bij vermenigvuldigen: -5 3a 6b 8c = -720abc 1) Vermenigvuldigen cijfers (let op teken) 2) Letters op alfabetische volgorde

Blog-Het gebruik van variabelen in Excel VBA

1 Rekenen met gehele getallen

Uitwerkingen Rekenen met cijfers en letters

8.1 Herleiden [1] Herleiden bij vermenigvuldigen: -5 3a 6b 8c = -720abc 1) Vermenigvuldigen cijfers (let op teken) 2) Letters op alfabetische volgorde

INLEIDING... 1 A FSPRAKEN... 2 INHOUDSOPGAVE...

Excel reader. Beginner Gemiddeld.

Inform 5-6. Toegepaste Informatica. Boekhouden-informatica Informaticabeheer. Deel 4a: Programmeren in VBA DAO-objecten (Access )

Datum. Vraag het bedrag in BEF. Reken om naar EURO. Toon het bedrag in EURO. --- Vraag het bedrag in BEF Reken om naar EURO---

3. Structuren in de taal

Computervaardigheden. Universiteit Antwerpen. Computervaardigheden en Programmatie. Grafieken en Rapporten 1. Inhoud. Wat is scripting?

Achtste college algoritmiek. 8 april Dynamisch Programmeren

Leren Programmeren met Visual Basic 6.0 Les 3+4. Hoofdstuk 4 : De Selectie

Datum, Tijd en Timer-object

Inhoud. Informatica. Hoofdstuk 5 Scripting. Context. Editor voor Programmeercode. Scripting 1

Programmeermethoden. Recursie. week 11: november kosterswa/pm/

Combinatoriek groep 1 & 2: Recursie

10 Meer over functies

Willem van Ravenstein

Breuken met letters WISNET-HBO. update juli 2013

Uiteenzetting Wiskunde Grafische rekenmachine (ti 83) uitleg

Examencursus. wiskunde A. Rekenregels voor vereenvoudigen. Voorbereidende opgaven VWO kan niet korter

Een korte samenvatting van enkele FORTRAN opdrachten

3.2 Basiskennis De getallenlijn Symbolen, tekens en getallen. 92 Algebra. Inhoofdstuk1zijnaandeordegeweest: Het=teken. =staat.

9.18 Macro s: oefeningen

Hoofdstuk 20. Talstelsels

Programmeermethoden. Recursie. Walter Kosters. week 11: november kosterswa/pm/

Basisvaardigheden algebra. Willem van Ravenstein Den Haag

REKENVAARDIGHEID BRUGKLAS

Stoomcursus. wiskunde A. Rekenregels voor vereenvoudigen. Voorbereidende opgaven VWO ( ) = = ( ) ( ) ( ) = ( ) ( ) = ( ) = = ( )

Achtste college algoritmiek. 12 april Verdeel en Heers. Dynamisch Programmeren

Hoofdstuk 7: Werken met arrays

VBA voor doe-het-zelvers

Machten, exponenten en logaritmen

Combinatoriek groep 2

Pascal uitgediept Data structuren

Hoofdstuk 20: Wiskundige functies

9.19 Macro s: oefeningen

Wortels met getallen en letters. 2 Voorbeeldenen met de (vierkants)wortel (Tweedemachts wortel)

1E HUISWERKOPDRACHT CONTINUE WISKUNDE

Small Basic Programmeren Text Console 2

Hoofdstuk 2: Werken met variabelen

Hoofdstuk 3: Keuzestructuren

2. Optellen en aftrekken van gelijknamige breuken

Uitleg: In de bovenstaande oefening zie je in het eerste blokje een LEES en een SCHRIJF opdracht. Dit is nog lesstof uit het tweede trimester.

Een spoedcursus python

Berekeningen op het basisscherm

Hoofdstuk 2: Werken met variabelen

3.1 Haakjes wegwerken [1]

Rekenen met cijfers en letters

Zomercursus Wiskunde. Module 1 Algebraïsch rekenen (versie 22 augustus 2011)

7 Omzetten van Recursieve naar Iteratieve Algoritmen

Domeinbeschrijving rekenen

Stel dat u 15 tellers nodig heeft. Dat kunt u een array van tellers als volgt declareren:

OPDRACHT Opdracht 2.1 Beschrijf in eigen woorden wat het bovenstaande PSD doet.

Basiskennis van machten WISNET-HBO. update juli 2007

Praktische toepassing van functies

Talstelsels, getalnotaties en Ascii code

Zestigdelige graden radialen honderddelige graden

1.1 Rekenen met letters [1]

VBA voor Doe het Zelvers Deel 7

Hoofdstuk 9: Menu s toevoegen

Zomercursus Wiskunde. Katholieke Universiteit Leuven Groep Wetenschap & Technologie. September 2008

Macro s. 4.2 Een macro maken

WISNET-HBO. update aug. 2011

De waarde van een plaats in een getal.

Vakgroep CW KAHO Sint-Lieven

In de tweede regel plaatsen we in het gereserveerde stukje geheugen een getal.

De uitleg in dit moduul is gebaseerd op een CASIO rekenmachine fx-82ms. Voor de verschillen met de TI-30X II zie de bijlage achterin.

Als een PSD selecties bevat, deelt de lijn van het programma zich op met de verschillende antwoorden op het vraagstuk.

Combinatoriek groep 1

extra oefening algoritmiek - antwoorden

Info-books. Toegepaste Informatica. Handleiding. Deel 40c : Gegevensbeheer en algoritmen in Access. HA40c. Jos Gils Erik Goossens

Je gaat leren programmeren en een spel bouwen met de programmeertaal Python. Websites zoals YouTube en Instagram zijn gebouwd met Python.

Practicum hoogtemeting 3 e klas havo/vwo

Numerieke benadering van vierkantwortels

5.7 Functies oefeningen

ANTWOORDEN blz. 1. d = 1013; = ; = ; =

1. Optellen en aftrekken

recursie Hoofdstuk 5 Studeeraanwijzingen De studielast van deze leereenheid bedraagt circa 6 uur. Terminologie

start -> id (k (f c s) (g s c)) -> k (f c s) (g s c) -> f c s -> s c

Breuksplitsen WISNET-HBO NHL. update juli 20014

HP Prime toetsenbord. HP Prime Graphing Calculator. Het toetsenbord van de HP-Prime

No part of this book may be reproduced in any form, by print, photoprint, microfilm or any other means without written permission of the publisher.

5. Functies. In deze module leert u:

ROGER FRANS. met cd. Conform module Rekenblad 3 van het leerplan INFORMATICA-TOEPASSINGSSOFTWARE voor het volwassenenonderwijs

Memoriseren: Een getal is deelbaar door 10 als het laatste cijfer een 0 is. Of: Een getal is deelbaar door 10 als het eindigt op 0.

1. REGELS VAN DEELBAARHEID.

Transcriptie:

Programmeren VBA 2

Programmeren VBA 2 Colofon: Uitgave 1.1 : M.M. Witkam, januari 2003 Nummer : 1022 Auteur : drs. M.M. Witkam Profieldeel : Profiel : Wiskunde Prijs : Niets uit deze uitgave mag verveelvuldigd en/of openbaar gemaakt worden door middel van druk, fotokopie, microfilm of op welke andere wijze dan ook, zonder voorafgaande schriftelijke toestemming van de auteur.

Programmeren VBA 2 INHOUDSOPGAVE 1. Procedures en functies, argumenten, recursie 1.1 Argumenten... 1 1.2 Recursie... 8 2 Gegevenstypen 2.1 Het type Variant, numerieke typen... 19 2.2 Het type String... 24 2.3 Rijen, matrices... 31 2.4 Zelfgedefinieerde typen... 33 3 Objecten 3.1 Klassemodules, eigenschappen... 39 3.2 Methoden... 43 4 Macro s in Word 4.1 Macro s opnemen... 51 4.2 Macro s bewerken en maken... 58 4.3 Macro s toewijzen aan sneltoets of menu... 63 5 Formulieren 5.1 Formulieren, besturingselementen, gebeurtenisprocedures... 67 5.2 Enkele voorbeelden... 74 5.3 Nog wat besturingselementen... 80 i

Programmeren VBA 2 ii

Programmeren VBA 2 Hoofdstuk 1 1. Procedures en functies, argumenten, recursie 1.1 Argumenten In hoofdstuk 5 van programmeren VBA 1 (verder afgekort als Pv1) heb je kennis gemaakt met de beginselen van het gebruik van procedures en functie in Visual Basic for Applications (verder afgekort als VBA). De daar gebruikte, enigszins beperkte, syntaxis was als volgt. [Private Public] Sub naam [(argumenten)] instructies End Sub [Public Private] Function naam [(argumenten)] [As type] instructies naam = expressie End Function Daarin was de, nog onvolledige, syntaxis van argument als volgt. [ByVal ByRef] varnaam [( )] As type We bekijken hier nog wat aspecten van het gebruik van procedures en functies, waarbij we ook de syntaxis een beetje ruimer nemen. Als je in de VBA-editor het woord Sub selecteert en op F1 drukt, krijg je de volledige syntaxis te zien, met de nodige toelichting. [Private Public] [Static] Sub naam [(argumenten)] [instructies] [Exit Sub] [instructies] End Sub Private en Public geven de zichtbaarheid van de procedure aan. Het sleutelwoord Static zullen we verder niet gebruiken. Ook Exit Sub, om een procedure vroegtijdig te verlaten, gebruiken we hier niet. Bij de omschrijving van argument zie je staan: [Optional] [ByVal ByRef] [ParamArray] varnaam [( )] [As type] [=standaardwaarde] Ook ParamArray gebruiken we niet, maar de rest wel. Je kent al het gebruik van ByVal en ByRef om argumenten via waarde of via verwijzing door te geven. De syntaxis suggereert met de rechte haken dat je dit ook weg kunt laten (het argument wordt dan via verwijzing doorgegeven), maar het is verstandig het expliciet te noemen. Het sleutelwoord Optional geeft aan dat een argument niet-verplicht of optioneel is. Dergelijke argumenten moeten altijd aan het einde van de lijst met argumenten staan. Vaak worden ze gebruikt om aanvullende gegevens door te kunnen geven, of extra informatie over hoe de eventuele (wel verplichte) argumenten behandeld moeten worden. Zo ziet bijvoorbeeld de procedure pvnr (overgang een op nieuwe regel) er als volgt uit. Sub pvnr(optional ByVal n As Integer = 1) ' overgang op nieuwe regel (alinea) Dim i As Integer For i = 1 To n Selection.TypeParagraph Next i End Sub ' pvnr 1

Programmeren VBA 2 Hoofdstuk 1 Daarin komt dus een niet-verplicht argument n voor. Dit wordt hier gebruikt om het aantal keren aan te geven dat er een nieuwe alinea moet worden ingevoegd. Hier geldt als standaardwaarde n = 1, dus als je de procedure oproept zonder argumenten, dan wordt er één nieuwe alinea ingevoegd. Wil je in één keer een aantal nieuwe alinea s invoegen, dan kun je dat als extra argument opgeven. We bekijken nog een (tamelijk onzinnig) voorbeeld. Voorbeeld 1 Sub Voorbeeld01() Dim a As Double, b As Double a = 2: b = 3 DoeIetsMet a DoeIetsMet a, b End Sub ' Voorbeeld01 Private Sub DoeIetsMet(ByRef a As Double, _ Optional ByRef b As Double) a = a + 1: b = b + 2 pvuitvoer "a = ", a, ", b = ", b, "." pvnr End Sub ' DoeIetsMet De procedure DoeIetsMet heeft een gewoon argument a, en een niet-verplicht argument b. Dit laatste heeft ook geen standaardwaarde. Bij het laten uitvoeren van dit voorbeeld, waarin de procedure DoeIetsMet twee keer wordt opgeroepen, krijg je te zien: a = 3, b = 2. a = 4, b = 5. De oorspronkelijke waarde 2 van a is dus, door de instructie a = a + 1 in de eerste oproep van de procedure DoeIetsMet, 3 geworden. Er was geen tweede argument opgegeven. Het argument b binnen DoeIetsMet blijkt de waarde 2 te hebben als gevolg van de instructie b = b + 2 dus het argument b moet binnen DoeIetsMet de waarde 0 gehad hebben. Dit is dus niet de waarde van de variabele b. Bij de tweede oproep van de procedure DoeIetsMet staan wel beide argumenten genoemd. Op dat moment heeft variabele a de waarde 3 en variabele b de waarde 3. Door de instructies a = a + 1: b = b + 2 in de procedure DoeIetsMet krijgt het argument a dus de waarde 4 en het argument b de waarde 5. Aan dit voorbeeld kun je zien dat je heel voorzichtig moet zijn met het gebruik van niet-verplichte argumenten, vooral als je geen standaardwaarde opgeeft. Je kunt dus het beste, zoals in de procedure pvnr, een redelijke standaardwaarde opgeven. Een alternatief is om met de ingebouwde functie IsMissing te kijken of een argument ontbreekt. Je zou hier dan iets kunnen schrijven als: If IsMissing(b) Then... Kijk voor meer informatie in de Visual Basic-editor: Help Index... IsMissing (Functie). 2

Programmeren VBA 2 Hoofdstuk 1 Een zinniger voorbeeld van een niet-verplicht argument staat hieronder. Voorbeeld 2 Sub Voorbeeld02() ' aantal cijfers achter de komma: Const ac = 6 ' de hoek in graden; een tellertje: Dim x As Double, i As Integer ' stel tabs in: pvzetrtabs 2: pvzetrtabs 5: pvzetrtabs 8 ' de kop van de tabel: pvuitvoer vbtab, "hoek", vbtab, "sinus" pvuitvoer vbtab, "cosinus": pvnr ' de tabel: For i = 1 To 18 x = 5 * i pvuitvoer vbtab, x, " " ' Alt-0176 pvuitvoer vbtab pvuitvoerr Sin(x, deg), ac ' of Gonio.Sin(x, deg) ' want: gedeclareerd in de module Gonio pvuitvoer vbtab pvuitvoerr Cos(x, deg), ac pvnr Next i pvwistabs End Sub ' Voorbeeld02 Daarin is de functie Sin met de argumenten x en hoekmaat gedeclareerd als: ' radialen of graden: Public Const rad As Integer = 0, deg As Integer = 1 Public Function Sin(ByVal x As Double, _ Optional hoekmaat As Integer = rad) _ As Double ' de sinus van een hoek in radialen of graden ' de omrekeningsfactor naar radialen: Dim y As Double, factor As Double If hoekmaat = rad Then factor = 1 Else factor = Pi / 180 End If y = VBA.Sin(x * factor) Sin = y End Function ' Sin De functie Cos (en Tan en Atn) is op een dergelijke manier gedefinieerd. Deze definities staan nu apart in de module Gonio. Door het gebruik van het niet-verplichte argument hoekmaat 3

Programmeren VBA 2 Hoofdstuk 1 kun je aangeven of de hoek in radialen (rad) of graden (deg) wordt opgegeven. Als je dit argument weglaat, dan wordt de hoekmaat gezien als radialen, zoals standaard in VBA. Je kunt hier ook zien hoe je naamconflicten kunt vermijden. Zolang de module Gonio zichtbaar is ( geopend is) verwijzen de namen Sin en Cos naar de functies in die module. Om toegang te krijgen tot de in VBA ingebouwde functies Sin en Cos moet je de naam laten voorafgaan door VBA en een punt, zoals hierboven in VBA.Sin. Verder zie je hoe je modules kunt gebruiken om een verzameling van verwante procedures en/of functies te groeperen. Als een bepaald programma veel van zulke procedures of functies oproept krijg je een beter leesbaar geheel door ze in een aparte module te zetten. Die module moet dan natuurlijk wel zichtbaar zijn. De in- en uitvoerprocedures zoals wij die hier gebruiken, staan daarom in het algemene documentsjabloon Normal.dot, dat altijd wordt geopend zodra Word wordt gestart. Kijk voor meer informatie in Microsoft Word: Help Index... sjablonen, Algemene sjablonen. Veel van de in VBA ingebouwde procedures en functies kennen niet-verplichte argumenten. Als je bijvoorbeeld de syntaxis van de functie MsgBox opvraagt krijg je het volgende te zien: MsgBox (prompt [, buttons] [, title] [, helpfile, context]) Blijkbaar is daarin het argument prompt verplicht, en zijn de andere argumenten niet-verplicht. Als je in de VBA-editor MsgBox en het haakje hebt getypt, dan krijg je min of meer dezelfde informatie ook te zien in het spiekbriefje : Op de argumenten HelpFile en Context gaan we hier niet verder in. Het verplichte argument Prompt staat voor de tekst in het berichtje. Niet verplicht is het argument Buttons, waarmee je kunt instellen welk pictogram en welke knoppen er moeten verschijnen; dit heeft blijkbaar als standaardwaarde vbokonly (alleen een OK-knop; dit wordt verder bij de hulp bij MsgBox verklaard). Ook niet verplicht is het argument Title, waarmee je de titel van het venstertje opgeeft. Als je dat weglaat, dan krijgt het venster als titel de naam van het programma van waaruit het wordt opgeroepen, dus hier Microsoft Word. Als je dus een berichtje wilt laten verschijnen met alleen een OK-knop, dan zou je kunnen schrijven: msg = MsgBox("Mededeling",, "Bericht") waarbij je de plaats van het tweede argument Buttons open laat. In dit soort situaties is het vaak handiger om benoemde argumenten te gebruiken. Je hoeft dan namelijk niet meer op de precieze positie van het argument te letten, en de bedoeling is vaak duidelijker. Je geeft een benoemd argument op door de naam van het argument, een dubbele punt, een gelijkteken en de waarde van het argument. De regel hierboven zou er dan zo kunnen uitzien. msg = MsgBox(Prompt:="Mededeling", Title:="Bericht") maar ook zo: msg = MsgBox(Title:="Bericht", Prompt:="Mededeling") Je hoeft dan dus alleen nog maar de namen van de argumenten te weten, en die verschijnen in het spiekbriefje zoals hierboven. Verder moet je natuurlijk weten welke argumenten verplicht zijn. 4

Programmeren VBA 2 Hoofdstuk 1 Voorbeeld 3 Sub Voorbeeld03() ' de tekst in het MsgBox-venster: Dim Tekst As String ' pictogram en knoppen: Dim knoppen As Integer ' de uitkomst van de functie: Dim msg As Integer Tekst = "Sinterklaas Kapoentje," & vbcr _ & "Gooi wat in mijn schoentje," & vbcr _ & "Gooi wat in mijn laarsje," & vbcr _ & "Dank u Sinterklaasje." & vbcr & vbcr _ & "Druk op een knop." knoppen = vbquestion + vbyesnocancel msg = MsgBox(Title:="Test MsgBox", _ buttons:=knoppen, _ Prompt:=Tekst) Select Case msg Case vbyes pvuitvoer "Je hebt op Ja gedrukt." Case vbno pvuitvoer "Je hebt op Nee gedrukt." Case vbcancel pvuitvoer "Je hebt op Annuleren gedrukt." End Select pvnr End Sub ' Voorbeeld03 Hier is dit gedemonstreerd met een wat flauw voorbeeld. Bij de functie MsgBox staan nu drie benoemde argumenten. Verder wordt ook gelezen welk knop is ingedrukt, waarop een passende reactie volgt. Als je een tekst op meer dan één regel in zo n venstertje wilt plaatsen, dan kun je zoals hier regeleinden (vbcr) in die tekst opnemen (dat werkt niet voor de tekst in de titelbalk). De totale tekst mag niet te lang zijn ( ongeveer 1024 tekens ). Het argument buttons heeft hier de waarde vbquestion + vbyesnocancel gekregen. Bij de helpinformatie over deze functie vind je nog andere mogelijke waarden. Je kunt die combineren door ze op te tellen, zoals hier gedaan is. Het programma reageert hier op de mogelijke functiewaarden vbyes, vbno of vbcancel. Ook hiervoor kun je bij de helpinformatie meer mogelijkheden vinden. Hiermee te vergelijken is de functie InputBox, waardoor een programma om invoer vraagt (de procedure pvinvoer maakt hiervan gebruik). De syntaxis is: InputBox (prompt [, title] [, default] [, xpos] [, ypos] [, helpfile, context]) Daarin is prompt weer de tekst in het venstertje, title de titel ervan, en default de functiewaarde die je krijgt als er niets is ingevoerd. Het argumenten xpos bepaalt de afstand van het invoervenster tot de linkerrand van het scherm, en het argument ypos de afstand tot de bovenrand. Deze waarden zijn in twips, waarbij één twip gelijk is aan 1/20 punt, en één punt 1/72 inch. Als je weet dat 1 inch gelijk is aan 25,4 mm, dan kun je uitrekenen dat één twip ongeveer gelijk is aan 0,017639 mm. Het is 5

Programmeren VBA 2 Hoofdstuk 1 misschien handiger om te weten dat 1 mm ongeveer 56,693 twips is. Je moet voorzichtig zijn met het opgeven van waarden voor deze argumenten. Als je niet uitkijkt staat het invoervenstertje buiten beeld te wachten op invoer. Voorbeeld 4 Sub Voorbeeld04() ' benoemde argumenten in InputBox ' de ingevoerde tekst: Dim Invoer As String Invoer = InputBox(Prompt:="Geef een tekst:", _ Title:="Voorbeeld InputBox", _ Default:="niets", _ xpos:=100, ypos:=100) pvuitvoer "De ingevoerde tekst is ", Invoer, "." pvnr End Sub ' Voorbeeld04 Er verschijnt nu een invoervenstertje vlak bij de linkerbovenhoek van het scherm met als vraag Geef een tekst: en als titel Voorbeeld InputBox. In het invoervak staat al (geselecteerd) de tekst niets. Als je iets invoert en op OK klikt (of op Enter drukt), dan wordt dat de waarde van de variabele Invoer. Als je op Annuleren klikt blijft de variabele Invoer leeg. Als je op deze manier een waarde voor een numerieke variabele wilt laten invoeren, dan kun je het beste de tekst naar een getal laten omzetten met een van de conversiefuncties zoals CInt of CDbl, die de tekst (zo mogelijk) omzetten naar een Integer resp. een Double. Dit geeft ook een foutmelding als de variabele leeg is. Je zou dat kunnen opvangen door een stukje programma zoals hieronder. If invoer <> "" Then getal = CInt(invoer) Else getal = 0 End If Kijk voor meer informatie in de Visual Basic-editor: Help Index... CInt (Functie) of CDbl (Functie). 6

Programmeren VBA 2 Hoofdstuk 1 Opgaven Opgave 1 Schrijf een programma dat met MsgBox een mededeling op het scherm zet. Laat de knoppen Nogmaals en Annuleren verschijnen, en kies een pictogram dat overeenkomt met de aard van de medeling (informatie, vraag, waarschuwing, paniek). Laat meedelen op welke knop gedrukt is, zoals in voorbeeld 3. Opgave 2* Schrijf een programma voor een simpel spelletje om getallen van 1 t/m 100 te raden. Gebruik InputBox om een getal te vragen, en laat het invoervenstertje rechtsboven op het scherm verschijnen. Laat in Word schrijven of het geraden getal te groot of te klein is. Laat net zo lang raden tot het juiste getal gevonden is. 7

Programmeren VBA 2 Hoofdstuk 1 1.2 Recursie In de wiskunde worden regelmatig recursieve definities gegeven, dat wil zeggen definities die grotendeels gebaseerd zijn op zichzelf. Als je aan een wiskundige vraagt wat 3 5 betekent, dan zou je als antwoord kunnen krijgen dat 3 5 = 3 4 3. Je krijgt dan dus in feite te horen dat machtsverheffen niets anders is dan een combinatie van vermenigvuldigen en machtsverheffen. Zo n antwoord is natuurlijk niet helemaal bevredigend. Als je verder zou vragen wat dan wel 3 4 is, loop je het risico dat je te horen krijgt: 3 4 = 3 3 3, en waarschijnlijk meteen daar achteraan: 3 n = 3 n 1 3, en als het goed is ook nog: 3 0 = 1 (of misschien 3 1 = 3). Met deze informatie kun je wel verder: 3 5 = 3 4 3 = 3 3 3 3 = 3 2 3 3 3 = 3 1 3 3 3 3 = 3 0 3 3 3 3 3 = 1 3 3 3 3 3 en dat kun je verder uitrekenen (wiskundigen doen dat meestal niet). In het algemeen is de macht van een getal x met een gehele exponent n in de wiskunde gedefinieerd als: x n = x n 1 x als n > 0; x n = 1 als n = 0; x n = 1 x n als n < 0 (dus n > 0). In VBA kun je dit op ongeveer dezelfde manier zeggen: Function macht1(byval x As Double, _ ByVal n As Integer) _ As Double ' x tot de macht n (recursief) If n >= 0 Then If n = 0 Then macht1 = 1 Else ' de recursieve oproep: macht1 = macht1(x, n - 1) * x End If Else macht1 = 1 / macht1(x, -n) End If End Function ' macht1 In de regel macht1 = macht1(x, n - 1) * x roept de functie dus zichzelf op, maar nu met argument n 1. Omdat het argument een keer gelijk aan 0 moet worden, volgt dan de niet-recursieve uitweg macht1 = 1 8

Programmeren VBA 2 Hoofdstuk 1 zoals in de wiskundige definitie hierboven. In dit geval is het natuurlijk niet moeilijk om een niet-recursieve functie te definiëren die precies hetzelfde doet, namelijk herhaald vermenigvuldigen. We spreken dan van een iteratieve definitie (recursief = teruglopend, iteratief = herhalend ). Function macht2(byval x As Double, _ ByVal n As Integer) _ As Double ' x tot de macht n (iteratief) ' de macht, een tellertje: Dim m As Double, i As Integer m = 1 If n >= 0 Then For i = 1 To n m = m * x Next i Else For i = 1 To -n m = m / x Next i End If macht2 = m End Function ' macht2 Als je de twee definities met elkaar vergelijkt zie je dat de recursieve net iets eenvoudiger is. Er zijn daar geen hulpvariabelen nodig, zoals in de iteratieve definitie, om de tel bij te houden, en om het op te bouwen produkt te onthouden. Voorbeeld 5 Sub Voorbeeld05a() ' bereken x tot de macht n met de twee functies ' grondtal en exponent: Dim x As Double, n As Integer ' macht (recursief en iteratief): Dim m1 As Double, m2 As Double pvinvoer "Grondtal:", x pvinvoer "Exponent:", n m1 = macht1(x, n) m2 = macht2(x, n) pvuitvoer "Recursief: " pvuitvoer x, "^", n, " = ", m1, "." pvnr pvuitvoer "Iteratief: " pvuitvoer x, "^", n, " = ", m2, "." pvnr End Sub ' Voorbeeld05a In dit voorbeeld worden beide functies gebruikt om een macht te berekenen. De uitkomsten zullen dus gelijk zijn. Om te zien of er misschien toch iets misgaat zou je kunnen kijken wat 9

Programmeren VBA 2 Hoofdstuk 1 er gebeurt als je een grondtal dicht bij 1 neemt, bijvoorbeeld 1,01, en een grote exponent. (Bij mij ging het toen mis bij exponenten iets boven de 5000). Verder kun je je natuurlijk afvragen hoe efficiënt zo n recursieve definitie is. Bij het tweede deel van dit voorbeeld (de procedure Voorbeeld05b) kun je opgeven van hoeveel grondtallen en exponenten je een macht wilt laten berekenen. Daarbij wordt de tijd opgenomen om dit (met beide functies) te doen. De machten zelf krijg je niet te zien. Je kunt wel zien dat de beide functies ongeveer even snel zijn. Hierin wordt gebruik gemaakt van de ingebouwde functie Timer (zonder argumenten), die het aantal seconden (een Single waarde) na middernacht geeft. Dit levert natuurlijk onbetrouwbare resultaten als je het experiment rond middernacht uitvoert. Sub Voorbeeld05b() ' bereken machten met de twee functies, ' en neem de tijd op ' begintijd en verstreken tijd: Dim begintijd As Single, tijd As Single ' aantal grondtallen en exponenten: Dim ag As Integer, an As Integer ' grondtal en exponent: Dim g As Integer, n As Integer ' macht (recursief en iteratief): Dim m1 As Double, m2 As Double pvinvoer "Aantal grondtallen:", ag pvinvoer "Aantal exponenten:", an pvuitvoer "Aantal grondtallen: ", ag, ", " pvuitvoer "aantal exponenten: ", an, ".": pvnr begintijd = Timer For g = 1 To ag For n = 1 To an m1 = macht1(g, n) Next n Next g tijd = Timer - begintijd pvuitvoer "Verstreken tijd (recursief): " pvuitvoerr tijd, 2 pvuitvoer " seconden.": pvnr begintijd = Timer For g = 1 To ag For n = 1 To an m2 = macht2(g, n) Next n Next g tijd = Timer - begintijd pvuitvoer "Verstreken tijd (iteratief): " pvuitvoerr tijd, 2 pvuitvoer " seconden.": pvnr End Sub ' Voorbeeld05b 10

Programmeren VBA 2 Hoofdstuk 1 Voorbeeld 6 Sub Voorbeeld06() ' bereken x tot de macht n; dit gaat dus mis. ' grondtal en exponent: Dim x As Double, n As Integer ' macht: Dim m As Double pvinvoer "Grondtal:", x pvinvoer "Exponent:", n m = macht(x, n) pvuitvoer x, "^", n, " = ", m, "." pvnr End Sub ' Voorbeeld06 Function macht(byval x As Double, _ ByVal n As Integer) _ As Double ' x tot de macht n (recursief, maar niet juist) macht = macht(x, n - 1) * x End Function ' macht In dit voorbeeld kun je zien wat er misgaat als je niet-recursieve uitweg bij een recursieve definitie vergeet. De functie blijft zichzelf oproepen totdat het daarvoor beschikbare geheugen (de stack ) op is, en je krijgt een foutmelding over Onvoldoende stackruimte. Als je op Foutopsporing klikt kom je in de VBA-editor, waar je kunt zien wat de waarde van n op dat moment is. Voorbeeld 7 Sub Voorbeeld07() ' een verkeerd gebruik van recursie ' twee getallen en hun som: Dim a As Integer, b As Integer, s As Integer ' voor MsgBox: Dim msg As Integer pvinvoer "Eerste getal:", a pvinvoer "Tweede getal:", b s = a + b pvuitvoer a, " + ", b, " = ", s, ".": pvnr msg = MsgBox(Prompt:="Nog een sommetje?", _ buttons:=vbyesno + vbinformation, _ Title:="Optellen") If msg = vbyes Then Voorbeeld07 End Sub ' Voorbeeld07 Dit is een typisch voorbeeld van het verkeerde gebruik van recursie. De procedure Voorbeeld07 roept steeds weer zichzelf aan, zonder niet-recursieve uitweg, en dat moet natuurlijk een keer misgaan. Omdat dat wel even kan duren zul je er waarschijnlijk niets van merken, wat eigenlijk nog veel vervelender is. 11

Programmeren VBA 2 Hoofdstuk 1 Er zijn situaties te bedenken waarin een bepaalde situatie recursief te beschrijven is, maar waarbij een recursief algoritme niet erg efficiënt is. Een voorbeeld daarvan vormen de getallen van Fibonacci. Dat is de rij getallen a 1 = 1, a 2 = 1, a 3 = 2, a 4 = 3, a 5 = 5, a 6 = 8, a 7 = 13, a 8 = 21, a 9 = 34,... waarbij elk getal, behalve de eerste twee, ontstaat door zijn twee voorgangers op te tellen: a 3 = a 1 + a 2 = 1 + 1 = 2, a 4 = a 2 + a 3 = 1 + 2 = 3, a 5 = a 3 + a 4 = 2 + 3 = 5, a 6 = a 4 + a 5 = 5 + 3 = 8, enz. Dit is natuurlijk prachtig recursief te formuleren: a n = a n 2 + a n 1 als n > 2; a n = 1 als n 2. In VBA wordt dat dus: Function fib1(byval n As Integer) As Long ' het n-de Fibonacci-getal (recursief) If n > 2 Then fib1 = fib1(n - 2) + fib1(n - 1) Else fib1 = 1 End If End Function ' fib1 Maar als je hier even over nadenkt zie je dat het gevaar zit in de regel fib1 = fib1(n - 2) + fib1(n - 1) waarin de functie twee keer wordt opgeroepen. Dat betekent bijvoorbeeld voor n = 9 dat de functie twee keer wordt opgeroepen, namelijk met n = 8 en n = 7. Elk van die oproepen geeft weer twee oproepen, zodat het uiteindlijk resulteert in 2 9 = 512 functie-oproepen (in werkelijkheid zijn het er iets minder, maar niet veel). Het lijkt dus de moeite waard om een niet-recursief algoritme te bedenken. Dit komt erop neer dat je op elk moment over de twee voorgangers moet beschikken om het getal zelf te kunnen berekenen. Als je de twee voorafgaande termen even t 1 en t 2 noemt, en het getal zelf f, dan heb je dus het volgende patroon, waarbij steeds f = t 1 + t 2. n t 1 t 2 f 1 1 2 1 1 3 1 1 2 4 1 2 3 5 2 3 5 6 3 5 8 7 5 8 13 Je kunt dit nog verder aanvullen tot n t 1 t 2 f 1 0 0 1 2 0 1 1 3 1 1 2 4 1 2 3 5 2 3 5 12

Programmeren VBA 2 Hoofdstuk 1 waarbij je, behalve op de eerste regel, weer steeds hebt f = t 1 + t 2, en waarbij de volgende regel uit de vorige ontstaat door de toewijzingen t1 = t2: t2 = f: f = t1 + t2 Hierdoor krijg je deze functie: Function fib2(byval n As Integer) As Long ' het n-de Fibonacci-getal (iteratief) ' de vorige termen: Dim t1 As Long, t2 As Long ' het getal: Dim f As Long ' tellertje: Dim k As Integer t1 = 0: t2 = 0: f = 1 For k = 2 To n t1 = t2: t2 = f: f = t1 + t2 Next k fib2 = f End Function ' fib2 Voorbeeld 8 In het onderstaande voorbeeld worden de beide functies opgeroepen. Geef een niet te grote waarde van n op, want anders moet je misschien wel erg lang wachten. Als je ook hier een tijdmeting wilt laten doen, dan kun je Voorbeeld08b laten uitvoeren. Sub Voorbeeld08a() ' bereken een Fibonacci-getal ' de waarde van n: Dim n As Integer ' het n-de Fibonaccigetal (recursief en iteratief): Dim f1 As Long, f2 As Long ' voor MsgBox: Dim msg As Integer pvinvoer "Fibonacci-getal; waarde van n:", n Do While n > 45 msg = MsgBox(Prompt:="Niet groter dan 45.", _ Buttons:=vbOKOnly + vbexclamation, _ Title:="Fibonacci") pvinvoer "Fibonacci-getal; waarde van n:", n Loop f1 = fib1(n) pvuitvoer "Recursief: " pvuitvoer "het ", n, "e Fibonaccci-getal is " pvuitvoer f1, ".": pvnr f2 = fib2(n) pvuitvoer "Iteratief: " pvuitvoer "het ", n, "e Fibonaccci-getal is " pvuitvoer f2, ".": pvnr End Sub ' Voorbeeld08a 13

Programmeren VBA 2 Hoofdstuk 1 Er zijn situaties waarin een recursief algoritme duidelijk de voorkeur verdient, en waarin het niet langzamer is dan een niet-recursief algoritme. Een bekend voorbeeld daarvan is een recept om een (1-dimensionale) matrix te laten sorteren. Dit staat bekend onder de naam Quick- Sort, omdat het zo ongeveer de snelste manier is om te sorteren. Private Sub Sorteer(ByRef a() As Integer, _ ByVal links As Integer, _ ByVal rechts As Integer) ' sorteer de matrix a van index links ' tot index rechts ' uit: N. Wirth, Algorithms + Data Structures = Programs ' ISBN 0-13-022418-9 ' het middelste element (tussen links en rechts): Dim m As Integer ' tellertjes; i loopt van links naar m ' j loopt van rechts naar m Dim i As Integer, j As Integer m = a((links + rechts) \ 2) i = links: j = rechts ' zorg dat alle elementen kleiner dan m links van ' m komen, en alle elementen groter dan m rechts: Do Do While a(i) < m i = i + 1 Loop Do While a(j) > m j = j - 1 Loop If i <= j Then Verwissel a(i), a(j) i = i + 1: j = j - 1 End If Loop Until i > j ' recursieve oproep; ' sorteer (zo nodig) de beide delen: If links < j Then Sorteer a(), links, j If rechts > i Then Sorteer a(), i, rechts End Sub ' Sorteer Als de ongesorteerde matrix bijvoorbeeld bestaat uit de getallen 10 12 14 7 5 6 13 dan wordt daar de middelste m waarde gekozen (hier dus m = 7). 10 12 14 7 5 6 13 Daarna wordt vanaf links gekeken totdat er een waarde groter dan m is gevonden, en vanaf rechts totdat er een kleinere waarde dan m wordt gevonden. Die getallen worden verwisseld. Na enige tijd ziet de matrix er dan zo uit, waarbij alle getallen kleiner dan m links van m staan, en alle getallen groter dan m rechts. 6 5 7 14 12 10 13 Als dat nog nodig is worden beide stukken van de matrix op dezelfde manier behandeld; de procedure wordt dus recursief opgeroepen. 14

Programmeren VBA 2 Hoofdstuk 1 Voorbeeld 9 Hier wordt de boven beschreven procedure opgeroepen. Enkele details (VulMatrix om de matrix te laten vullen met willekeurige getallen, SchrijfMatrix om de matrix te laten schrijven) zijn hier weggelaten. ' een 1-dimensionale matrix sorteren ' (van klein naar groot) ' de matrix: Dim a() As Integer ' het aantal elementen: Dim n As Integer Sub Voorbeeld09a() ' sorteer een 1-dimensionale matrix (QuickSort) ' een tellertje: Dim i As Integer ' invoer, initialisatie: pvinvoer "Aantal elementen van de matrix:", n Randomize ReDim a(1 To n) ' vul de matrix: VulMatrix a ' schrijf de matrix: pvuitvoer "De getallen:": pvnr SchrijfMatrix a ' sorteer de matrix: Sorteer a(), 1, n ' resultaten: pvuitvoer "Gesorteerd:": pvnr SchrijfMatrix a End Sub ' Voorbeeld09a Als je het sorteren in actie wilt zien, dan kun je de procedure Voorbeeld09b laten uitvoeren, waarin je ook de tussenstappen (het deel van de matix dat gesorteerd wordt en de verwisselingen) kunt bekijken. Neem in dat geval niet teveel getallen in de matrix. Het kan ook gebeuren dat procedures of functies wederzijds recursief zijn, dat wil zeggen dat ze elkaar oproepen. Voorbeeld 10 Van twee rijen getallen a n en b n is gegeven: a 0 = 0,5 en b 0 = 0,5; a n = 0,7a n 1 + 0,2b n 1 als n > 0; b n = 0,3a n 1 + 0,8b n 1 als n > 0. Je kunt dit ook in matrixvorm schrijven: a 0 b = 0,5 0 0,5 a n en b = 0,7 0,2 n 0,3 0,8 a n 1 b. n 1 15

Programmeren VBA 2 Hoofdstuk 1 Hieronder is dit in VBA omgezet met behulp van twee functies a en b, die zichzelf en elkaar oproepen. ' aantal stappen: Dim n As Integer ' de beginwaarden: Dim a0 As Double, b0 As Double ' de eindwaarden: Dim an As Double, bn As Double ' de overgangsmatrix : Const p1 As Double = 0.7, q1 As Double = 0.2 Const p2 As Double = 0.3, q2 As Double = 0.8 Sub Voorbeeld10() pvinvoer "Beginwaarde a0:", a0 pvinvoer "Beginwaarde b0:", b0 pvinvoer "Aantal stappen:", n an = a(n): bn = b(n) pvuitvoer "Beginwaarde a0 = ", a0, "." pvnr pvuitvoer "Beginwaarde b0 = ", b0, "." pvnr pvuitvoer "Aantal stappen n = ", n, "." pvnr pvuitvoer "Eindwaarde an = ", an, "." pvnr pvuitvoer "Eindwaarde bn = ", bn, "." pvnr End Sub ' Voorbeeld10 Private Function a(byval n As Integer) As Double If n > 0 Then a = p1 * a(n - 1) + q1 * b(n - 1) Else a = a0 End If End Function ' a Private Function b(byval n As Integer) As Double If n > 0 Then b = p2 * a(n - 1) + q2 * b(n - 1) Else b = b0 End If End Function ' b Deze manier om het probleem in VBA te vertalen heeft overigens dezelfde bezwaren als de recursieve definitie van de Fibonacci-getallen uit voorbeeld 8. Het aantal functie-oproepen neemt ongeveer exponentieel toe met de waarde van n. Ook hier is een iteratieve versie veel efficiënter. 16

Programmeren VBA 2 Hoofdstuk 1 Opgaven Opgave 3 Schrijf een recursieve functie om n! (n-faculteit) te berekenen. Dit getal is gedefinieerd door n! = (n 1)! n als n > 0; n! = 1 als n = 0. Schrijf ook een iteratieve versie voor deze functie. Laat de functies oproepen in een simpel programma, waarbij n maximaal de waarde 170 kan krijgen (het getal 170! past nog net in een Double variabele). Opgave 4 Schrijf een recursieve functie om het aantal cijfers (voor de komma) van een positief reëel getal te berekenen. Bedenk daarbij dat voor dit aantal cijfers ac onder andere geldt: ac(x) = 1 + ac x 10. Schrijf ook een iteratieve versie van deze functie, en een versie waarbij je gebruik maakt van logaritmen (met grondtal 10). Laat de functies oproepen in een simpel programma. Opgave 5* Schrijf een iteratief programma voor het probleem van voorbeeld 10. Laat in één enkele procedure a n en b n berekenen. Opgave 6* In Pv1 (hoofdstuk 5, voorbeeld 7) is een iteratieve versie gegeven voor het berekenen van de grootste gemene deler van twee gehele getallen m en n met het algorimte van Euclides. Deze ging als volgt: Function ggd(byval m As Long, ByVal n As Long) _ As Long ' de grootste gemene deler van m en n (iteratief) Dim r As Long r = m Mod n Do While r > 0 m = n: n = r r = m Mod n Loop ggd = n End Function ' ggd Als je bijvoorbeeld neemt m = 222 en n = 162, dan krijg je achtereenvolgens: m n r 222 162 60 162 60 42 60 42 18 42 18 6 18 6 0 De juistheid van dit algoritme is te verklaren uit het feit dat op elk moment geldt: ggd(m, n) = ggd(n, r) en ggd(n, 0) = n, waarbij r = m Mod n. 17

Programmeren VBA 2 Hoofdstuk 1 Gebruik dit laatste om een recursieve functie voor de functie ggd te schrijven. Laat die functie, en de iteratieve, oproepen in een simpel programma. 18

Programmeren VBA 2 Hoofdstuk 2 2. Gegevenstypen 2.1 Het type Variant, numerieke typen Als je meer informatie opvraagt over het declareren van variabelen, dan krijg je onder andere de volledige syntaxis van de instructie Dim te zien: Dim [WithEvents] varnaam [([subscripts])] [As [New] type] [, [WithEvents] varnaam [([subscripts])] [As [New] type]]... Kijk voor (nog) meer informatie in de Visual Basic-editor: Help Index... Dim (Instructie). Wij zullen ons in het algemeen beperken tot: Dim varnaam [([subscripts])] [As type]... Daarin komt dus voor het sleutelwoord Dim, de naam van de variabele, eventueel haakjes en subscripts (bij matrices), en (ook eventueel) het sleutelwoord As en het type van de variabele. Dat betekent dus dat je in principe het type mag weglaten. Zo is bijvoorbeeld Dim m, n As Integer een syntactisch correcte declaratie. Je hebt hiermee alleen niet twee variabelen van het type Integer gedeclareerd, maar een variabele n van het type Integer en een variabele m zonder type. Dit betekent dat m het type Variant krijgt, een beetje vaag type. In variabelen van het type Variant kunnen waarden opgeslagen worden van numerieke typen, maar ook bijvoorbeeld tekenreeksen (Strings). Uit de context van het programma maakt VBA dan op hoe de betreffende variabele behandeld moet worden. Kijk voor meer informatie in de Visual Basic-editor: Help Index... Variant (Gegevenstype). Voorbeeld 1 Sub Voorbeeld01() Dim a, b, c ' of: ' Dim a As Variant, b As Variant, c As Variant ' a, b en c worden getallen: a = 23: b = 58: c = a + b pvuitvoer "a = ", a, ", " pvuitvoer "b = ", b, ", " pvuitvoer "c = ", c, ".": pvnr ' a, b en c worden Strings: a = "23": b = "58": c = a & b pvuitvoer "a = ", a, ", " pvuitvoer "b = ", b, ", " pvuitvoer "c = ", c, ".": pvnr End Sub ' Voorbeeld01 Hier wordt dus uit de context opgemaakt dat a, b en c eerst numerieke variabelen zijn, en later Strings. 19

Programmeren VBA 2 Hoofdstuk 2 Het is in het algemeen natuurlijk niet verstandig om VBA uit te laten zoeken wat je eigenlijk bedoelt. Zorg ervoor dat je dat altijd zelf expliciet zegt. Maar er zijn wel situaties te bedenken waarbij pas tijdens het uitvoeren van het programma blijkt welk type een bepaalde variabele uiteindelijk moet hebben, en dat kun je dan moeilijk al van tevoren opgeven. Voorbeeld 2 Sub Voorbeeld02() ' een deling Dim a As Double, b As Double, c As Variant pvinvoer "Deling; teller:", a pvinvoer "Deling; noemer:", b If b <> 0 Then c = a / b Else c = "niets" End If pvuitvoer a, "/", b, " = ", c, ".": pvnr End Sub ' Voorbeeld02 Hier krijgt c dus, als de noemer niet nul is, de numerieke waarde van het quotiënt. Als de noemer wel nul is, dan krijgt c als waarde de tekst niets. Zo nodig kun je de functie IsNumeric gebruiken om te zien of een variabele een waarde heeft die als getal kan worden geïnterpreteerd. Met de functie TypeName kun je bepalen welk type een variabele heeft. Kijk voor meer informatie in de Visual Basic-editor: Help Index... IsNumeric (Functie) en TypeName (Functie). In een variabele van het type Variant kun je dus onder andere numerieke waarden opslaan. De numerieke typen Byte, Integer, Long, Single en Double ken je al. We noemen nog even kort enkele karakteristieken. naam soort bereik precisie Byte geheel getal 0 t/m 255 exact Integer geheel getal 32.768 t/m 32.767 exact Long geheel getal 2.147.483.648 t/m 2.147.483.647 exact Single reëel getal 1,401298E 45 tot 3,402823E38 6 à 7 cijfers positief en negatief Double reëel getal 4,94065645841247E 324 t/m 1,79769313486232E308 (en negatief) 14 à 15 cijfers Met gehele getallen wordt altijd exact gerekend (binnen het bereik). Met reële getallen hoeft dat niet te gebeuren. Heel grote en heel kleine reële waarden worden als drijvende-komma getallen voorgesteld; bijvoorbeeld: 1,401298E 45 = 1,401298 10 45 ; 1,79769313486232E308 = 1,79769313486232 10 308. Er zijn nog een paar numerieke typen, die in bepaalde situaties handiger zijn. Als je veel met data (meervoud van datum) en tijden moet werken, kun je het beste het type Date gebruiken. Je kunt daarin data opslaan vanaf 1 januari 0100 t/m 31 december 9999, en tijdstippen van 00.00.00 t/m 23.59.59 uur. 20

Programmeren VBA 2 Hoofdstuk 2 Intern worden zulke variabelen voorgesteld als reële getallen, waarbij het deel voor de komma de datum aangeeft, en het deel na de komma het tijdstip als deel van de hele dag. Het nulpunt ligt op 30 december 1899, dus negatieve getallen stellen een eerdere datum voor en positieve getallen een latere. Zo komt bijvoorbeeld het getal 1,5 overeen met de datum 31 december 1899 (1 dag na 30 december 1899) en het tijdstip 12.00.00 (0,5 dag na middernacht). We zullen dit gegevenstype verder niet gebruiken. Kijk voor meer informatie in de Visual Basic-editor: Help Index... Date (Gegevenstype), Weekday (Functie), Year (Functie), Month (Functie), Day (Functie), Hour (Functie), Minute (Functie), Second (Functie). Als je regelmatig met (grote) geldbedragen moet werken is het gegevenstype Currency handig. Hierin kunnen waarden worden opgeslagen van 922.337.203.685.477,5808 t/m 922.337.203.685.477,5807 (dus maximaal 15 cijfers voor de komma en 4 cijfers erachter). Met deze waarden wordt, binnen het bereik, exact gerekend. Gezien de aard van deze variabelen zijn eigenlijk alleen optellen en aftrekken zinvol, evenals vermenigvuldigen met of delen door een reëel getal. Het onderling vermenigvuldigen of delen van zulke variabelen heeft feitelijk geen betekenis. Bovendien kun je dan ook vrij eenvoudig de bovengrens overschrijden ( overloop of overflow ). Kijk voor meer informatie in de Visual Basic-editor: Help Index... Currency (Gegevenstype). Voorbeeld 3 Sub Voorbeeld03() ' optellen en aftrekken van Currency-variabelen ' de getallen: Dim a As Currency, b As Currency ' som en verschil: Dim som As Currency, verschil As Currency pvinvoer "Eerste getal:", a pvinvoer "Tweede getal:", b som = a + b: verschil = a - b pvuitvoer a, " + ", b, " = " pvuitvoer som, ".": pvnr pvuitvoer a, " - ", b, " = " pvuitvoer verschil, ".": pvnr End Sub ' Voorbeeld03 Nog een numeriek type is Decimal. Je kunt in variabelen van dit type getallen opslaan die (in absolute waarde) variëren van 0,0000000000000000000000000001 t/m 79.228.162.514.264.337.593.543.950.335 met een precisie van 28 à 29 cijfers. Dit zijn wel drijvende-komma getallen, in tegenstelling tot waarden van het type Currency. Je kunt variabelen van het type Decimal niet rechtstreeks declareren. Als je een variabele van dit type wilt gebruiken, moet je hem declareren als Variant, en hem later omzetten ( converteren ) naar het type Decimal. Ook bij toewijzings- en rekeninstructies doe je er verstandig aan steeds expliciet te laten omzetten naar Decimal. Dit doe je met de functie CDec. 21

Programmeren VBA 2 Hoofdstuk 2 Voorbeeld 4 Sub Voorbeeld04() ' invoer van, en rekenen met het type Decimal ' de twee getallen: Dim a As Variant, b As Variant ' som en verschil: Dim som As Variant, verschil As Variant ' produkt en quotiënt: Dim produkt As Variant, quotient As Variant ' functiewaarden (Sin en Atn): Dim y1 As Variant, y2 As Variant InvoerDecimal a InvoerDecimal b som = CDec(a + b): verschil = CDec(a - b) pvuitvoer a, " + ", b, " = ", som, ".": pvnr pvuitvoer a, " - ", b, " = ", verschil, ".": pvnr produkt = CDec(a * b): quotient = CDec(a / b) pvuitvoer a, " ", b, " = ", produkt, ".": pvnr pvuitvoer a, " ", b, " = ", quotient, ".": pvnr ' dit geeft Double waarden: y1 = CDec(Sin(a)): y2 = CDec(Atn(b)) pvuitvoer "Sin(", a, ") = ", y1, ".": pvnr pvuitvoer "Atn(", b, ") = ", y2, ".": pvnr End Sub ' Voorbeeld04 Private Sub InvoerDecimal(ByRef x As Variant) ' invoer van één variabele van het type Decimal; ' bij lege invoer wordt de waarde 0 ' de ingevoerde tekst: Dim tekst As String tekst = InputBox(Title:="Invoer Decimal", _ Prompt:="Geef een decimale waarde:") If tekst <> "" Then x = CDec(tekst) Else x = 0 End If End Sub ' InvoerDecimal In de invoerprocedure zie je hoe je een variabele van het type Decimal laat invoeren: je voert een stukje tekst in en je laat het met de functie CDec omzetten naar een Decimal. Omdat lege tekst niet naar een numerieke waarde kan worden omgezet wordt dit geval apart afgehandeld. Bij het berekenen van som, verschil en verder wordt ook steeds expliciet omgezet naar Decimal. Hierdoor ziet een programma met Decimals er altijd een beetje geforceerd uit. Je zou dit kunnen verstoppen in aparte procedures. Als je het programma laat uitvoeren zul je opmerken dat de functiewaarden van Sin en Atn niet de nauwkeurigheid van een Decimal hebben, maar die van een Double. Daardoor is het nut van dit gegevenstype voor berekeningen met dergelijke functies beperkt. Kijk voor meer informatie over Decimal in de Visual Basic-editor: Help Index... Decimal (Gegevenstype). 22

Programmeren VBA 2 Hoofdstuk 2 Er zijn overigens nog een aantal type-conversiefuncties, namelijk CBool, CByte, CCur, CDate, CDbl, CDec, CInt, CLng, CSng, CVar en CStr. De naam van de functie geeft daarbij aan naar welk type de functie converteert. Kijk voor meer informatie over conversiefuncties in de Visual Basic-editor: Help Index... CDec (Functie). Na dit overzicht kun je concluderen dat er een vrij grote keuze is in numerieke typen. Behalve de al bekende typen Byte, Integer en Long (voor gehele getallen) en Single en Double (voor reële getallen) zijn er ook nog Currency en Decimal, die ieder enkele specifieke voordelen hebben, vooral op het gebied van de nauwkeurigheid. Gezien de bedoeling van deze laatste twee typen is het nut ervan toch vaak beperkt. Meestal zul je dus voldoende mogelijkheden hebben met de al bekende typen. Vooral bij het rekenen met reële getallen kun je het beste maar gewoon Double variabelen gebruiken. Verder kun je het type Variant beter niet gebruiken, behalve als daar een goede reden voor is. Die is er bijvoorbeeld als je een procedure wilt schrijven om de waarden van twee variabelen te verwisselen, zoals in sorteerprocedures regelmatig gebeurt. Je kunt dit dan formuleren zoals hieronder. Sub Verwissel(ByRef x As Variant, ByRef y As Variant) Dim hulp As Variant hulp = x: x = y: y = hulp End Sub ' Verwissel 23

Programmeren VBA 2 Hoofdstuk 2 2.2 Het type String Je kent variabelen van het gegevenstype String al als variabelen waarin je tekst (in VBA ook wel tekenreeksen genoemd) kunt opslaan. Strings kunnen een variabele of een vaste lengte hebben. Een String met variabele lengte moet je declareren als Dim S As String Een String met een vaste (maximum)lengte van bijvoorbeeld 5 declareer je als Dim S As String * 5 De enige bewerking die tot nu toe genoemd is, is het aan elkaar plakken van stukken tekst met de bewerking &. Verder kun je strings (alfabetisch) met elkaar vergelijken met behulp van = en < en dergelijke vergelijkingsoperatoren. Zo is bijvoorbeeld "AB" < "AC" "AB" < "ABC" "A" < "a" VBA kent ook nog wat handige functies om tekst verder te manipuleren, waarvan we er een paar bekijken. Voorbeeld 5a Sub Voorbeeld05a() ' de functie Len ' een String: Dim S As String ' het aantal tekens: Dim lengte As Long S = InputBox(Title:="String", _ Prompt:="Geef een stukje tekst:") lengte = Len(S) pvuitvoer "De tekst is ", S, ".": pvnr pvuitvoer "Het aantal tekens is ", lengte, ".": pvnr End Sub ' Voorbeeld05a Met de functie Len kun je de lengte (het aantal tekens) van een string laten berekenen. De maximale lengte van een string met variabele lengte is ongeveer 2 miljard (2 31 ), en van een string met vaste lengte ongeveer 65000 (2 16 ). Voorbeeld 5b Sub Voorbeeld05b() ' de functies LCase en UCase Dim S As String, T As String S = "ABCde01234ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞ" pvuitvoer "De tekst is: ", S: pvnr ' zet om in kleine letters: T = LCase(S) pvuitvoer "In kleine letters: ", T: pvnr S = "fghij56789àáâãäåæçèéêëìíîïðñòóôõöøùúûüýþ" pvuitvoer "De tekst is: ", S: pvnr ' zet om in hoofdletters: T = UCase(S) pvuitvoer "In hoofdletters: ", T: pvnr End Sub ' Voorbeeld05b 24

Programmeren VBA 2 Hoofdstuk 2 Met de functie LCase (afkorting van Lower Case) laat je de letters in een string omzetten in kleine letters; met de functie UCase (afkorting van Upper Case) zet je de letters om in hoofdletters. Dit werkt ook voor letters met accenten en andere lettertekens die in sommige (West-) Europese alfabetten gebruikt worden, zoals de IJslandse letters Ð (eth) en Þ (thorn) met bijbehorende kleine letters ð en þ (ð klinkt ongeveer als de th in het Engelse this, en þ klinkt ongeveer als de th in het Engelse thing). Voorbeeld 5c Sub Voorbeeld05c() ' de functies Left, Right en Mid ' het aantal tekens uit de tekst: Const at As Integer = 10 ' twee Strings: Dim S As String, T As String ' de lengte van S: Dim lengte As Long S = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" pvuitvoer "De tekst is: ", S: pvnr lengte = Len(S) pvuitvoer "De lengte is: ", lengte: pvnr T = Left(S, at) pvuitvoer "De eerste ", at, " letters zijn: " pvuitvoer T: pvnr T = Right(S, at) pvuitvoer "De laatste ", at, " letters zijn: " pvuitvoer T: pvnr T = Mid(S, 1 + (lengte - at) \ 2, at) pvuitvoer "De middelste ", at, " letters zijn: " pvuitvoer T: pvnr End Sub ' Voorbeeld05c Met de functies Left, Right en Mid kun je stukken uit een string halen. De syntaxis van de functie Left is Left (string, length) Deze functie geeft als resultaat een Variant (String) met het opgegeven aantal tekens length vanaf de linkerkant van de tekenreeks string. Net zo is de syntaxis van Right Right (string, length) waarmee je het opgegeven aantal tekens vanaf de rechterkant van de string krijgt. De functie Mid is iets ingewikkelder. De syntaxis is Mid (string, start [, length]) Hiermee krijg je een aantal tekens uit string, vanaf positie start. Het niet-verplichte argument length geeft aan hoeveel tekens uit de string je krijgt. Als je dit argument weglaat krijg je alle tekens uit de string vanaf positie start. Ook als de string vanaf start minder tekens bevat dan length krijg je alle tekens vanaf positie start. Deze functie is onder andere handig als je de aparte tekens uit de string wilt bewerken. Het teken op positie n in string S vind je dan namelijk met Mid(S, n, 1) 25

Programmeren VBA 2 Hoofdstuk 2 Voorbeeld 5d Sub Voorbeeld05d() ' de functie Instr ' een String en één letterteken: Dim S As String, c As String * 1 ' de positie van c in S: Dim p As Long S = InputBox(Title:="String", _ Prompt:="Geef een stukje tekst:") c = InputBox(Title:="String", _ Prompt:="Geef één letter:") pvuitvoer "De tekst is: ", S, ".": pvnr p = InStr(S, c) Do While p > 0 pvuitvoer "De letter ", c, " staat " pvuitvoer "op positie ", p, ".": pvnr p = InStr(p + 1, S, c) Loop End Sub ' Voorbeeld05d De functie Instr geeft de positie waarop een string de eerste keer voorkomt in een andere string. De (iets vereenvoudigde) syntaxis is InStr ([start, ] string1, string2) Daarin is string1 de string waarin wordt gezocht, en string2 de string waarnaar wordt gezocht. Het niet-verplichte argument start geef de positie in string1 aan waar het zoeken begint; als je dit weglaat wordt vanaf het begin van string1 gezocht. Als string2 inderdaad voorkomt in string1, dan geeft de functie de beginpositie van string2 in string1 (vanaf positie start). Zo niet, dan geeft de functie de waarde 0. In het voorbeeld wordt eerst gekeken of teken c wel in string S voorkomt door de positie p te laten berekenen met p = InStr(S, c) Zolang p groter dan nul is komt c voor in S. Om te zien of c nog verder in S voorkomt wordt gekeken of c nog voorkomt vanaf positie p + 1, p = InStr(p + 1, S, c) dus één plaats verder dan waar c al was gevonden. Als c niet meer voorkomt krijgt p de waarde 0, en stopt de herhaling. 26

Programmeren VBA 2 Hoofdstuk 2 Voorbeeld 5e Sub Voorbeeld05e() ' de functie Hex (hexadecimaal, 2-complement) ' de ingevoerde tekst: Dim invoer As String ' het getal en de hexadecimale voorstelling: Dim getal As Long, S As String invoer = InputBox(Title:="Hexadecimaal", _ Prompt:="Geef een geheel getal:") getal = CLng(invoer) S = Hex(getal) pvuitvoer "Het getal is ", getal, ".": pvnr pvuitvoer "Hexadecimaal is dit ", S, ".": pvnr End Sub ' Voorbeeld05e Als je regelmatig met hexadecimale (zestientallige) getallen werkt is de functie Hex handig. Deze maakt van een geheel getal een string die de hexadecimale voorstelling van dat getal voorstelt, in 2-complement notatie en met maximaal 8 hexadecimale cijfers. Het getallenbereik is dus 2.147.483.648 ( 2 31 ) t/m 2.147.483.647. Niet-gehele getallen worden afgerond. Je kunt overigens hexadecimale (gehele) getallen van maximaal 8 cijfers rechtsreeks gebruiken door een getal te laten voorafgaan door &H. Zo stelt bijvoorbeeld het getal &H13A het decimale getal 314 voor. Dergelijke waarden zijn wel numeriek. Je zou dus in het bovenstaande voorbeeld zo n getal kunnen invoeren. Je kunt de verschillende string-functies ook gebruiken om een string te ontleden. Je kunt dan bijvoorbeeld een stukje tekst laten invoeren, en daaruit alle gehele getallen aflezen, zoals hieronder. Voorbeeld 6 ' constanten op module-niveau; Const cijfers As String = "01234567890" Const tekens As String = "+-" Sub Voorbeeld06() ' haal alle gehele getallen op uit een String ' de String: Dim S As String ' het gehele getal daarin (als tekst): Dim GetalTekst As String ' het gehele getal (als waarde): Dim getal As Double ' de positie in S: Dim p As Long ' één letterteken in S: Dim c As String * 1 27

Programmeren VBA 2 Hoofdstuk 2 ' bijvoorbeeld: S = "abc+123 +- 456pq xy12-34+x-12,34" pvuitvoer "De tekst is: ", S, ".": pvnr ' bekijk de tekst vanaf het eerste letterteken: p = 1: c = Mid(S, p, 1) Do While p < Len(S) GetalTekst = "" If InStr(tekens & cijfers, c) > 0 Then ' er staat een teken of een cijfer GetalTekst = GeheelGetal(S, p) Else p = p + 1 End If If GetalTekst <> "" Then getal = CDbl(GetalTekst) pvuitvoer "Gevonden: ", getal, ".": pvnr End If If p <= Len(S) Then c = Mid(S, p, 1) Loop End Sub ' Voorbeeld06 Private Function GeheelGetal(ByRef S As String, _ ByRef p As Long) _ As String ' vorm een geheel getal uit de tekens van S, ' vanaf positie p; ' na afloop is de positie p die van het eerste ' teken na het getal; ' als het getal uit alleen een + of een - ' bestaat, is de functiewaarde leeg. ' één letterteken in S: Dim c As String * 1 ' de tekst die een getal voorstelt: Dim Tekst As String c = Mid(S, p, 1) If InStr(tekens, c) Then ' er staat een + of een - Tekst = c p = p + 1 SpatiesWeg S, p c = Mid(S, p, 1) End If Do While InStr(cijfers, c) Tekst = Tekst & c p = p + 1: c = Mid(S, p, 1) Loop If Tekst = "+" Or Tekst = "-" Then GeheelGetal = "" Else GeheelGetal = Tekst End If End Function ' GeheelGetal 28

Programmeren VBA 2 Hoofdstuk 2 Private Sub SpatiesWeg(ByRef S As String, _ ByRef p As Long) ' één letterteken in S: Dim c As String * 1 c = Mid(S, p, 1) Do While c = " " p = p + 1: c = Mid(S, p, 1) Loop End Sub ' SpatiesWeg De tekst wordt hier letter voor letter bekeken. Als er een letter staat die het begin van een getal zou kunnen zijn, dus een cijfer of een plus- of minteken, dan neemt de functie GeheelGetal het over. Daarin worden de volgende letters van de tekst bekeken zolang dit cijfers zijn, en die worden aan het getal toegevoegd. Mochten aan het einde blijken dat er alleen een plus of een min stond, dan wordt een lege string als functiewaarde opgeleverd. Een dergelijke techniek is bruikbaar als een programma een aantal gegevens in een bepaald formaat verwacht, bijvoorbeeld gehele getallen. Het ontwerpen van de invoerprocedure is dan wel wat bewerkelijker, maar het programma is eenvoudiger in het gebruik. Kijk voor meer informatie over String-functies in de Visual Basic-editor: Help Index... Asc (Functie), Chr (Functie), Format (Functie), LTrim (Functie), RTrim (Functie), Space (Functie), StrConv (Functie), String (Functie), Trim (Functie). 29

Programmeren VBA 2 Hoofdstuk 2 Opgaven Opgave 1 Schrijf een programma met als invoer een stukje tekst, en als uitvoer dezelfde tekst maar met elke letter op een aparte regel (alinea). Opgave 2 Schrijf een programma met als invoer een stukje tekst, en als uitvoer de tekst van achteren naar voren. Gebruik daarbij een functie Omgekeerd, die de tekst omkeert. Opgave 3 Schrijf een programma met als invoer een hexadecimaal (16-tallig) getal en als uitvoer het bijbehorende decimale (10-tallige) getal. In het hexadecimale getal is A = 10, B = 11, C = 12, D = 13, E = 14, F = 15. Het hexadecimale getal wordt als String opgegeven en het decimale getal numeriek. Bijvoorbeeld: 3A1C (hexadecimaal) = 14876 (decimaal). Gebruik het volgende algoritme. 3 A 1 C 48 928 14864 3 58 929 14876 16 Dus: neem het eerste cijfer (3), vermenigvuldig dit met 16 (3 16 = 48) en tel daarbij het tweede cijfer (A = 10) op (48 + 10 = 58); vermenigvuldig het resultaat met 16 (58 16 = 928) en tel daarbij het derde cijfer (1) op (928 + 1 = 929); enzovoort. Als het cijfer een decimaal cijfer (0 t/m 9) is dan is er geen probleem. Als het cijfer 10 of groter is (A t/m F) dan moet je dit laten omzetten naar een decimaal getal. Dit gaat het makkelijkst met de ingebouwde functie Asc, die van een letterteken de numerieke code geeft. Aan die code zelf heb je niet veel, maar wel aan het feit dat de letters A, B, C, D, E en F opeenvolgende codes hebben, dus: Asc ("B") Asc ("A") = 1, Asc ("C") Asc ("A") = 2, enz. Natuurlijk moet je controleren of het ingevoerde getal wel bestaat uit alleen hexadecimale cijfers 0 t/m 9 en A t/m F. Verder is het handig om het ingevoerde getal (met de functie UCase) te laten omzetten in hoofdletters. Opgave 4 Schrijf een programma met als invoer een breuk in de vorm a b/c (als string), en als uitvoer de drie getallen die de breuk vormen. Bij een invoer 2 5/7 moet het programma dus melden dat het gehele deel 2 is, de teller 5 en de noemer 7. Verder moet het programma correct reageren op een invoer zoals 2/3 (gehele deel 0, teller 2, noemer 3), en ook op de invoer van een geheel getal zoals 5 (gehele deel 5, teller 0, noemer 1). Verwijder eerst (met de functie Trim) de eventuele spaties aan het begin en het einde van de string. Kijk daarna (met de functie InStr) of er een breukstreep / in de string voorkomt, en zo ja, waar (als er een breukstreep voorkomt, dan is het deel rechts daarvan de noemer van de breuk). Kijk dan ook of er een spatie in de string voorkomt (als er een spatie voorkomt, dan is dat de scheiding tussen het gehele deel en de teller). Komt er geen spatie of breukstreep voor, dan is het een geheel getal. Gebruik de functies Left, Right en Mid om de verschillende delen van de breuk te vinden. Laat deze strings omzetten in numerieke waarden. Houd er rekening mee dat een lege string niet rechtstreeks kan worden omgezet in een getal. 30