Betinget logik – er det logisk?

 Betinget logik med If-sætninger
     Nested If's

Betinget logik med Select Case

    Nested Selects

 

Har du arbejdet med Excel mere end på bare det mest grundlæggende niveau, vil du typisk være stødt på betinget logik. Dette ses nemlig jævnligt i formler. En formel som fx =HVIS(A1<A2;"Ja";"Nej") er en betinget logisk formel. Funktionsnavnet er HVIS(), og derefter følger som argument til funktionen en betingelse, A1<A2 (eller på dansk A1 mindre end A2). Dernæst det resultat, der skal fremkomme, hvis betingelsen er opfyldt (udsagnet Sandt) og til sidst det resultat, der skal fremkomme, hvis betingelsen ikke er opfyldt (udsagnet er Falsk). Alle logiske funktioner har nemlig kun to muligheder, enten er de sande eller også er de falske. Hver af de to Værdi_Hvis argumenter, klan erstattes af nye HVIS'er, fx

 

=HVS(A1<A2;"Mindre";HVIS(A1=A2);"Lig med";"Større"))

 

Her tester vi først på om A1 er mindre end A2. Er den det, skriver vi "Mindre". Er der den ikke mindre, tester vi igen, denne gang om de to celler er lig med hinanden. Er de det, skriver vi "Lig med", er de ikke skriver vi "Større". Det sidste behøver vi ikke at teste for, da A1 enten er mindre end, lig med eller større end A2. Sådanne Hvis'er inden i Hvis'er kaldes "Indlejrede Hvis'er" eller "Nested Ifs" på engelsk. I en almindelig formel, kan der højest være 7 indlejrede Hvis'er i samme sætning. Skal der være flere må man ty til andre løsninger.

 

I selve regnearket findes andre logiske funktioner, fx SAND, FALSK, IKKE, ELLER og OG. Sand og Falsk bruges typisk i kombination med andre funktioner. De returnerer henholdsvis værdien Sand og værdien Falsk. OG tester på om flere betingelser er opfyldt samtidigt. =OG(A1>A2;A1<A3) vil være sand, hvis A1 er større end A2 samtidigt med at den er mindre end A3. I modsat fald vil den være Falsk. Omvendt vil ELLER være sand, hvis bare én af betingelserne er opfyldt. =ELLER(A1>A2;A1<A3) vil fx være sand selv om A1 er større end både A2 og A3. IKKE() "vender en sandhedsværdi". Med tallene 3, 4 og 5 i henholdsvis A1, A2 og A3 vil =OG(A1>A2;A1<A3) returnere Falsk. Kombinerer vi med IKKE, til =IKKE(OG(A1<A2;A1>A3)) "vendes" sandhedsværdien, og der returneres i stedet Falsk. IKKE bruges ofte i kombination med en række andre logiske funktioner i regnearket. Hvis man fx vil teste på, om indholdet af en celle en fejlkode, kan man bruge =ER.FEJL(A1). Det vil returnere Sand, hvis der står en fejlkode i A1, Falsk, hvis der ikke gør. Imidlertid har vi af og til brug, kun at gøre noget, hvis der ikke står en fejlkode. Det kan så løses med =HVIS(IKKE(ER.FEJL(A1));"Gør noget").

 

Hvorfor nu al den snak om almindelige regnearksfunktioner i en artikel, der handler om programmering? Såmænd kun for at afmystificere begrebet "betinget logik", inden vi går i gang. Alt det, man kan lave i regnearket med almindelige funktioner, kan man også lave i VBA med kode – foruden en del, som ikke umiddelbart kan laves i regnearket.

 

Excel har et par måder at lave logiske betingelser på, som vi skal nærmere på i denne artikel. Den ene er nok ikke overraskende, "Hvis" eller "If" på vbask, den anden er en funktion, der kaldes "Select Case". I denne artikel vil jeg også komme ind på nogle få af Excels indbyggede funktioner, flere følger i senere artikler, men hvis du vil "helt til bunds" i de indbyggede funktioner, må du nok ty til onlinehjælpen. Jeg kender ingen bøger, der beskriver samtlige indbyggede funktioner i VBA, det er der simpelthen for mange til. Vi skal også se på VBAs svar på ELLER og OG mm.

 

Ovenfor forklarede jeg, at alle logiske funktioner altid returner enten Sand eller Falsk. Det gælder også i VBA. Man taler ofte om funktioners "Sandhedstabeller". Det er simpelthen oversigter i tabelform, over hvornår forskellige kombinationer af svar er sande og falske. Klik evt her for at se eksempler på sandhedstabeller. Det er ikke sikkert, at du umiddelbart forstår dem, men det kommer du forhåbentlig til, inden du er færdig med denne artikel.

 

Betinget logik med If sætninger.

Lad mig starte med et simpelt eksempel. I en makro findes to variable, som begge tildeles en værdi. Vi vil nu vide om variabel A er større end eller lig med B, eller om B er større end A:

 

Sub Betinget1()

    Dim bytA As Byte, bytB As Byte

   

    bytA = 12

    bytB = 14

   

    If bytA >= bytB Then

        MsgBox "Variabel A er større end variabel B eller de to er lige store"

    Else

        MsgBox "Variabel B er større end variabel A"

    End If

End Sub

 

I dette tilfælde vises en meddelelsesboks med teksten: "Variabel B er større end variabel A" og en OK-knap. Prøv at bytte om på variablernes størrelse og se hvad der så sker J.

 

If-sætningen spørger om, hvilken variabel, der er størst. Efter "Then", skriver vi så, hvad der skal ske, hvis udsagnet er Sandt. "Else" fortæller, hvad der skal ske, hvis udsagnet er Falsk. I dette tilfælde er det tilstrækkeligt, da der kun er to muligheder. Enten er A større end eller lig med B, eller også er den det ikke.

 

En konstruktion af denne type kaldes en blok IF, og den skal altid sluttes med sætningen "End If". Det er muligt at skrive korte If-sætninger på en enkelt linje, fx

 

If Bruger1 = "Ole" Then MsgBox "Ole" Else MsgBox "En anden"

 

I sådanne tilfælde, skal der ikke afsluttes med End If.

 

Lad mig vende tilbage til det første eksempel. Hvis vi nu gerne vil have én besked, hvis variabel A er større end B og én anden, hvis den er lig med B, kan følgende bruges.

 

Sub Betinget2()

    Dim bytA As Byte, bytB As Byte

   

    bytA = 12

    bytB = 14

   

    If bytA > bytB Then

        MsgBox "Variabel A er større end variabel B"

    ElseIf bytA = bytB Then

        MsgBox "Variabel A har samme større som variabel B"

    Else

        MsgBox "Variabel B er større end variabel A"

    End If

   

End Sub

 

Her introducerer vi et nyt udtryk, nemlig ElseIf som gør det muligt at have mere end to udfald i en If-sætningen. Der kan være adskillige ElseIf udtryk i hver If-sætning, men man vil ofte anvende Select Case, se nedenfor, hvis der er mange valgmuligheder, der skal testes for.

 

ElseIF kan fx bruges, hvis vi skal teste på svar fra meddelelsesbokse med mere end to knapper.

 

Forestil dig følgende: Vi har en meddelelsesboks med vbYesNoCancel, altså tre forskellige knapper at klikke på. Vi ved (se artiklen om kommunikation med makroer), at hver knap giver forskellige returværdier,  Ja = vbYes, Nej = vbNo og Annuller = vbCancel. Disse svar repræsenteres af talværdierne 6, 7, og 2. De kan derfor "opbevares" i en variabel, erklæret som Byte.

 

Sub JaNejAnnuller()

   

    Dim bytSvar As Byte

   

    bytSvar = MsgBox("Kan De lide østers?", vbYesNoCancel + vbQuestion)

   

    If bytSvar = vbYes Then

        MsgBox "Østers kan give madforgiftning", vbEinformation + vbOKOnly

    ElseIf bytSvar = vbNo Then

        MsgBox "Østers er ellers godt for potensen", vbInformation + vbOKOnly

    Else

        MsgBox "De vil åbenbart ikke vide noget om østers!" _

            , vbExclamation + vbOKOnly

    End If

   

End Sub

 

Her testes der på, hvilken knap brugeren klikker på. Da der kun er tre muligheder behøver vi ikke at kende returværdien fra den ene knap. Den kommer bare via Else-delen. Læg mærke til, at man faktisk også kan bruge Annuller-knappen til at "gøre noget". Normalt vil den imidlertid bare blive brugt til at Annullere handlingen, og det gør den også her, men viser lige en meddelelsesboks først.

 

Læg mærke til indrykningen. Jeg har talt lidt om det i andre artikler, at det er en god idé at indrykke sine kodelinjer. På den måde kan man se, hvad der hører sammen. Det, der står indrykket under If hører til denne linje, det, der er indrykket under ElseIf hører til denne og så videre. Fordelen er, at man nemmere kan overskue kodens struktur. Dette bliver endnu mere aktuelt, når man skal arbejde med flere linjer, nested ifs, løkker og kombinationer af disse. Et andet "skriveråd" er, at man aldrig må være bange for "luft", altså tomme linjer.  At indsætte tomme linjer i koden medvirker også til at gøre denne langt mere læselig.

 

Hvad kan man så bruge som betingelser? Det kan som ovenfor være variabler, det kan være indhold af regnearket, input fra inputbokse og meget andet. Og hvad kan man undersøge? Ja, i princippet kan man teste på alt, der resulterer i en Sand eller Falsk returværdi, evt. en Null-værdi. Null er et ord, der oprindeligt stammer fra engelsk (amerikansk) jura. Det betyder, at noget ikke har nogen juridisk værdi, jf udtrykket "declare the contract null and void", der fortæller at en kontrakt ingen juridisk betydning har, fx fordi den er indgået under tvang. Void har samme betydning som Null, så det er bare en forstærkning. I matematikken repræsenter Null ofte værdien 0 (nul), men i programmering repræsenteres 0 selvstændigt, og der skelnes mellem nul og Null (udtales nol, med o som i kom). I denne sammenhæng betyder Null, at der slet ikke eksisterer en værdi. Dette må ikke forveksles med, at der eksisterer en værdi, som tilfældigvis bare er tom.

 

Et eksempel på forskellen på Null og Empty er fx hvis vi skal registrere Fornavn, Mellemnavn og Efternavn for en person. Vi har to personer: Jens Ole Nielsen og Hans Jensen. På det tidspunkt, hvor vi skal registrere Jens ved vi, at han har et mellemnavn, men vi kender det ikke, og bør derfor lade dette være tomt. Han HAR derimod ikke noget mellemnavn, og værdien er derfor en Null-værdi.

 

I VBA kan man teste på forskellig vis, fx med sammenligningsoperatorer eller logiske operatorer. Desuden kan man bruge en række indbyggede funktioner, hvis resultat i sig er Sand eller Falsk.  I nedenstående tabel repræsenterer S sammenligningsoperatorer. L repræsenter logiske operatorer og IF repræsenter indbyggede funktioner.

 

Operator

Type

Betydning

=

S

Tester om to værdier er lig med hinanden.

<> 

S

Tester om to værdier er forskellige fra hinanden

S

Tester på om værdien til venstre for tegnet er større end værdien til højre for tegnet.

>=

S

Tester på om værdien til venstre for tegnet er større end eller lig med værdien til højre for tegnet.

S

Tester på om værdien til venstre for tegnet er mindre end værdien til højre for tegnet.

<=

S

Tester på om værdien til venstre for tegnet er mindre end eller lig med værdien til højre for tegnet.

And

L

Tester for om flere betingelse begge er opfyldt. I så fald returneres True.

Or

L

Tester for om mindst en af flere betingelser er opfyldt. I så fald returneres True.

Xor

L

Tester på om netop én af flere betingelser  er opfyldt. I så fald returneres True.

Not

L

Hvis udtrykket er False, returneres True og omvendt.

IsNull()

IF

Returner True, hvis udtrykket i parentesen er en Null-værdi.

IsArray()

IF

Returner True hvis variablen i parentesen er et array.

IsDate()

IF

Returner True hvis værdien i parentesen er en dato eller et klokkeslæt i et af de formater som VBA anerkender.

IsEmpty()

IF

Returner True hvis værdien i parentesen er tom, typisk en tom tekststreng.

IsError()

IF

Returner True hvis værdien i parentesen er en fejlværdi.

IsMissing()

IF

Tester om et valgfrit argument i en funktionsprocedure ikke er udfyldt. Denne funktion returnerer ikke True, hvis argumentet er en tal-datatype, da variabler med disse datatyper altid indeholder 0, hvis ikke de udfyldes. Det er derfor en god ide undtagelsesvis at erklære valgfri argumenter som Variant.

IsNumeric()

IF

Returner True hvis værdien i parentesen er et tal.

isObject()

IF

Returner True hvis variablen i parentesen er et objekt.

Vartype()

IF

Returner en værdi, der fortæller datatypen på variablen i parentesen. Se i Online hjælpen til VBA under "VarType Function" for at se de forskellige returværdier.

 

 

Lad mig se på bare et par eksempler på noget af ovenstående:

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

 

If strA <> strB Then

    MsgBox "Du har ikke indtastet samme værdi"

End If

 

Her testes alene på om de to værdier er forskellige.

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

 

If strA <= strB Then

    MsgBox "Den første værdi er mindst"

End If

 

Her testes på om strA er mindre end strB. Alle sammenligningsoperatorerne virker på samme måde. I hvert tilfælde kan der selvfølgelig også indsættes en Else-sætning med det, der skal ske, hvis If-sætningen returnerer False.

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

strC = InputBox("Indtast en tredje værdi")

 

If strA <= strB And strB < strC Then

    MsgBox "B er mellem A og C"

End If

 

Her skal B både være større end A og mindre end C for at MsgBox'en vises.

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

strC = InputBox("Indtast en tredje værdi")

 

If strA <= strB Or strB < strC Then

    MsgBox "B er enten større end A eller mindre end C eller begge dele"

End If

 

Her vises meddelelsesboksen, hvis B enten er større end A, mindre end C eller begge dele på én gang.

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

strC = InputBox("Indtast en tredje værdi")

 

If strA <= strB Xor strB < strC Then

    MsgBox "B er enten større end A eller mindre end C"

End If

 

Her vises MsgBox'en kun, hvis B er større end A eller mindre end C, men den vises ikke, hvis B både er større end A eller mindre end C på én gang.

 

strA = InputBox("Indtast en værdi")

 

If IsNumeric(strA) Then

    MsgBox "Du har indtastet et tal"

End If

 

Her vises meddelelsesboksen kun, hvis der indtastes et tal.

 

strA = InputBox("Indtast en værdi")

strB = InputBox("Indtast en anden værdi")

strC = InputBox("Indtast en tredje værdi")

 

If Not IsNumeric(strA) Or Not IsNumeric(strB) Or Not IsNumeric(strC) Then

    MsgBox "Du kan kun regne, hvis alle variable er tal"

    Exit Sub

Else

    MsgBox strA * strB / strC

End If

 

Her testes om der indtastes værdier, der IKKE er tal. Hvis bare én af de indtastede værdier ikke er et tal, vises en MsgBox'en. Her er det vigtigt at huske om der skal bruges And eller Or. Bruger man And i ovenstående procedure, vises meddelelsesboksen kun, hvis alle tre variabler er tekster, og den vil derfor stoppe udførelsen, hvis man fx taster, 7, 9 og T. Udtrykket Exit Sub får makroen til at stoppe uden at udføre mere. I dette tilfælde sker det, hvis der er indtastet en ikke-numerisk værdi, da det vil få makroen til at fejle.

 

- Til top -

 

Nested If's

Af og til skal der undersøges for flere ting, og afhængigt af udfaldet af den første undersøgelse, skal der så undersøges for nye ting. Det kræver flere If's, og håndteres ved at anbringe flere If-sætninger inde i hinanden.

 

Nedenstående kode, spørger om man vil købe en bil eller en cykel? Svarer man Nej (Cykel) får man at vide, at cykelsalget et indstillet. Svarer man Ja (Bil) spørges om bilen skal leveres med (Nej) eller uden (Ja) moms. Svares der Nej, omstilles man til afdelingen for private kunder. Svares der Ja, spørges om man har en momsregistreret virksomhed. Svarer man Ja, omstilles til salgsafdelingen for erhvervskøretøjer. Svarer man Nej, er man en svindler og indberettes til Toldvæsenet!

 

NB! I de følgende kodeeksempler er der en række ret lange sætninger. For at gøre det muligt at kopiere direkte fra skærmen til eget modul, har jeg delt sætningerne med _ (underscore) i lidt flere korte dele, end jeg normalt ville gøre. I virkeligheden kan man skrive delte linjer helt ud, men det gør koden meget svær at læse på skærmen.

 

Sub NestedIf()

    Dim bytBilCykel As Byte

    Dim bytMoms As Byte

    Dim bytMomsReg As Byte

   

    bytBilCykel = MsgBox("Klik Ja for at bestille en bil, Nej for at" _

        & " bestille en cykel", vbYesNo + vbQuestion)

   

    If bytBilCykel = vbYes Then

        bytMoms = MsgBox("Klik Ja, hvis bilen skal leveres uden moms, " _

        & "Nej, hvis den skal leveres med moms", vbQuestion + vbYesNo)

       

        If bytMoms = vbYes Then

            bytMomsReg = MsgBox("Er du momsregisreret?", vbQuestion + vbYesNo)

           

            If bytMomsReg = vbYes Then

                MsgBox "Du omstilles nu til salgsafdeling for " _

                    & "erhvervskøretøjer", vbOKOnly + vbInformation

            Else

                MsgBox "Vi sælger kun uden moms til momsregistrede " _

                    & "virksomheder" & vbCrLf _

                    & "Dine data sendes nu til Told og Skat" _

                   , vbCritical + vbOKOnly

            End If

                  

        Else

            MsgBox "Du omstilles nu til afdelingen for salg af private" _

                & "køretøjer", vbInformation + vbOKOnly

        End If

    Else

        MsgBox "Desværre, cykelsalget er pt. indstillet på grund" _

        & " af storm", vbInformation + vbOKOnly

    End If

          

End Sub

 

Læg mærke til, at indrykningerne gør det nemmere at overskue, hvad der hænger sammen og hvad der er på forskellige "If-niveauer".

 

Lad mig slutte med endnu er eksempel, om end ikke helt så "nested".

 

Her vil vi undersøge, hvad der står i en bestemt celle i regnearket. Dette gør vi med Range-objektet. Jeg vender tilbage til brugen af objekter i en senere artikel. I denne omgang må man bare stole på, at det, jeg skriver, faktisk virker J. På grundlag af indholdet af cellen suppleret med en meddelelsesboks, udfører vi så nogle betingede sætninger. Her ser jeg først trinvis på opbygnmingen, og derefter sætter jeg det hele sammen.

 

I celle A1 skal tastes et af navnene Svend, Knud eller Valdemar. Dette skal gøres inden makroen afspilles. Det første makroen gør er at teste om der faktisk er tastet noget i celle A1.

 

Dette kan gøres med

 

Dim strNavn As String

 

If IsEmpty(Range("a1").Value) Then

    MsgBox "Der skal være tastet et navn i A1." _

        & "Du skal taste enten Svend, Knud eller Valdemar" _

        , vbInformation + vbOKOnly

 

I dette tilfælde fortæller meddelelsesboksen også, hvad det er "lovligt" at taste i cellen. Når der ikke afsluttes med End If, er det fordi sætningen endnu ikke er færdig. Hvis cellen ikwek er tom, skal vi have testet om det, der står, er en af de tilladte værdier:

 

Dim strNavn As String

 

strNavn = ActiveSheet.Range("a1").Value

 

If IsEmpty(Range("a1").Value) Then

    MsgBox "Der skal være tastet et navn i A1." _

        & "Du skal taste enten Svend, Knud eller Valdemar" _

        , vbInformation + vbOKOnly

Else

    strNavn = ActiveSheet.Range("a1").Value

    If strNavn <> "Svend" And strNavn <> "Knud" And strNavn <> "Valdemar" _

        Then

        MsgBox "Du skal taste enten Svend, Knud eller Valdemar i A1" _

         , vbInformation + vbOKOnly

 

Nu sker det faktisk jævnligt, at der tastes "svend"  eller "SVEND" i stedet for "Svend", og hvis der gør det, vil man få fejlmeddelelsen alligevel. Det samme gælder selvfølgelig for "knud" eller "VALDEMAR". Dette skyldes at VBA er case sensitiv (følsom overfor brugen af store og små bogstaver). Hvis man ønsker at "genere" brugeren så lidt som muligt, er det en god idé at konvertere til store bogstaver inden vi tester. På den måde undgås problemet. VBA har en indbygget funktion, UCase, der gør netop dette. Jeg retter derfor tildelingssætningen til

 

strNavn = UCase(ActiveSheet.Range("a1").Value)

 

og betingelseslinjen til

 

If strNavn <> "SVEND" And strNavn <> "KNUD" And strNavn <> "VALDEMAR" Then

 

Derefter er det ligegyldigt, hvordan navnene indtastes. De oversættes til store bogstaver, og sammenlignes med navnene, der jo også er skrevet med stort.

 

Nu skal vi så til at teste, hvilket af de tre navne, der faktisk er tastet, og så gøre forskelligt afhængig af, hvad der står. Dette ville jeg normalt gøre med en en Select Case, og der viser jeg i næste afsnit. Her vil jeg dog bruge end If…Then…ElseIf...Else sekvens.

 

Denne sekvens ser således ud:

 

           If strNavn = "SVEND" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Grathe"

                MsgBox "Svend tabte slaget på Grathe Hede i 1157 " _

                    & "til Valdemar. Svend selv blev dræbt af en bonde" _

                    , vbOKOnly

               

            ElseIf strNavn = "KNUD" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Knud have intet " _

                    & "tilnavn"

                MsgBox "Knud blev dræbt af Svends folk under Blodgildet" _

                    & " i Roskilde. Det lykkes for Valdemar at undslippe." _

                    , vbOKOnly

               

            Else

                Range("a1").Offset(0, 1).Value = "Tilnavn: Den Store"

                MsgBox "Valdemar var søn af Knud Lavard. Efter Svend og" _

                    & " Knuds død blev han enekonge i hele landet.", vbOKOnly

               

            End If

 

Når navnet er genkendt, skrives i cellen ved siden af A1 (Range("a1").Offset(0, 1).Value) kongens eventuelle tilnavn. Desuden vises en meddelelsesboks  med kort information om den pågældende konge.

 

Metoden Offset svarer til funktionen Forskydning() i Excel. Den flytter et antal celler i forhold til udgangspunktet. Det første tal i parentesen er antallet af rækker, det andet antallet af kolonner. Positive tal flytter Ned (rækker) eller til højre (kolonner). Negative tal flytter modsætningsvis op eller til venstre.

 

Value angiver, at det, der kommer efter lighedstegnet skal skrives i cellen. Value kan udelades i en række sammenhænge, fx denne og den sidste Offset kunne skrives som:

 

Range("a1").Offset(0, 1) = "Tilnavn: Den Store"

 

Imidlertid er der også tilfælde hvor Value skal med. Jeg vælger derfor normalt altid at skrive det. Det gør jeg også i tildelingssætningen for at indikere, at det er cellens værdi, jeg er interesseret i. Value er en af de egenskab er, som objektet range kan have. Jeg vender som sagt tilbage til objekter i en senere artikel.

 

Den samlede kode kommer så til at se ud som følger:

 

Sub FindNavn()

    Dim strNavn As String

 

    If IsEmpty(Range("a1")) Then

        MsgBox "Der skal være tastet et navn i A1." _

            & "Du skal taste enten Svend, Knud eller Valdemar", vbInformation + vbOKOnly

    Else

           strNavn = UCase(ActiveSheet.Range("a1").Value)

           If strNavn <> "SVEND" And strNavn <> "KNUD" And strNavn <> "VALDEMAR" Then

                Range("a1").Offset(0, 1).Value = ""

                MsgBox "Du skal taste enten Svend, Knud eller Valdemar i A1" _

            , vbInformation + vbOKOnly

        Else

            If strNavn = "SVEND" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Grathe"

                MsgBox "Svend tabte slaget på Grathe Hede i 1157 til Valdemar." _

                    & " Svend selv blev dræbt af en bonde", vbOKOnly

               

            ElseIf strNavn = "KNUD" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Knud have intet tilnavn"

                MsgBox "Knud blev dræbt af Svends folk under Blodgildet i Roskilde." _

                    & " Det lykkes for Valdemar at undslippe.", vbOKOnly

               

            Else

                Range("a1").Offset(0, 1).Value = "Tilnavn: Den Store"

                MsgBox "Valdemar var søn af Knud Lavard. Efter Svend og Knuds død" _

                    & " blev han enekonge i hele landet.", vbOKOnly

               

            End If

        End If

    End If

End Sub

 

Læg i øvrigt mærke til, at jeg først tildeler streng-variablen en værdi, når jeg er sikker på, at der faktisk står noget i cellen.  Jeg kunne også have gjort det inden testen for, om der er tastet noget, men i så fald vil If IsEmpty ikke virke, da tekststrengen eksisterer. I stedet kunne jeg så teste på om strengen var tom:

 

Sub FindNavn()

    Dim strNavn As String

           strNavn = UCase(ActiveSheet.Range("a1").Value)

    If strNavn = "" Then

        MsgBox "Der skal være tastet et navn i A1." _

            & "Du skal taste enten Svend, Knud eller Valdemar" _

            , vbInformation + vbOKOnly

Osv.

 

Til sidst et lille resume af If-sætningens struktur:

 

If betingelse Then

     If-kode

ElseIF betingelse Then

     ElseIf kode

(ElseIf sætninger kan udelades eller gentages efter behov)

Else

    Else kode

End if         

 

- Til top -

Select Case

Select Case en særdeles anvendelig, hvis der skal testes på flere forskellige mulige udfald af en betingelse. I eksemplet ovenfor testede jeg på en af tre forskellige navne, efter først at have testet for tom celle eller forkert navn:

 

            

            If strNavn = "SVEND" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Grathe"

                MsgBox "Svend tabte slaget på Grathe Hede i 1157 til Valdemar." _

                    & " Svend selv blev dræbt af en bonde", vbOKOnly

               

            ElseIf strNavn = "KNUD" Then

                Range("a1").Offset(0, 1).Value = "Tilnavn: Knud have intet tilnavn"

                MsgBox "Knud blev dræbt af Svends folk under Blodgildet i Roskilde." _

                    & " Det lykkes for Valdemar at undslippe.", vbOKOnly

               

            Else

                Range("a1").Offset(0, 1).Value = "Tilnavn: Den Store"

                MsgBox "Valdemar var søn af Knud Lavard. Efter Svend og Knuds død" _

                    & " blev han enekonge i hele landet.", vbOKOnly

 

Er der mange navne kan det hurtigt blive uoverskueligt og i andre sammenhænge kan det også blive uoverskueligt selv om det ikke er så mange valgmuligheder. I stedet kan bruge Select Case.  Denne sætning har denne struktur

 

Select Case (udtrykket der skal testes)

     Case

     Case-kode

      (Case sætningen kan gentages efter behov)

     Case Else

     Else-kode

End Select

 

Vi kan i stedet for ovenstående løse HELE opgaven med

 

Sub SelCas()

    Select Case UCase(ActiveSheet.Range("a1").Value)

        Case Is = ""

            MsgBox "Der skal være tastet et navn i A1." _

                & "Du skal taste enten Svend, Knud eller Valdemar" _

                , vbInformation + vbOKOnly

       

        Case "SVEND"

            Range("a1").Offset(0, 1).Value = "Tilnavn: Grathe"

            MsgBox "Svend tabte slaget på Grathe Hede i 1157 til Valdemar." _

                & " Svend selv blev dræbt af en bonde", vbOKOnly

 

        Case "KNUD"

            Range("a1").Offset(0, 1).Value = "Tilnavn: Knud have intet tilnavn"

            MsgBox "Knud blev dræbt af Svends folk under Blodgildet " _

                & "i Roskilde. Det lykkes for Valdemar at undslippe.", vbOKOnly

 

        Case "VALDEMAR"

            Range("a1").Offset(0, 1).Value = "Tilnavn: Den Store"

            MsgBox "Valdemar var søn af Knud Lavard. Efter Svend og " _

                & "Knuds død blev han enekonge i hele landet.", vbOKOnly

 

        Case Else

            Range("a1").Offset(0, 1).Value = ""

            MsgBox "Du skal taste enten Svend, Knud eller Valdemar i A1" _

                , vbInformation + vbOKOnly

    End Select

End Sub

 

Select Case har andre fordele frem for If. Blandt andet er det nemmere at teste på intervaller.

 

Se fx på følgende if-struktur

 

Sub IntervalTestIf()

    Dim intA As Integer

   

    intA = InputBox("Indtast et tal mellem 0 og 100 begge incl") '

   

    If intA >= 0 And intA < 10 Then

        MsgBox "A er lille"

    ElseIf intA >= 10 And intA < 30 Then

        MsgBox "A er under middel"

    ElseIf intA >= 30 And intA < 70 Then

        MsgBox "A er middel"

    ElseIf intA >= 70 And intA < 90 Then

        MsgBox "A er over middel"

    ElseIf intA >= 90 And intA <= 100 Then

        MsgBox "A er stor"

    Else

        MsgBox "Du skal taste tal mellem 0 og 100"

    End If

End Sub

 

Og sammenlign så med Select Case strukturen

 

Sub IntervalTestSelect()

    Dim intA As Integer

   

    intA = InputBox("Indtast et tal mellem 0 og 100 begge incl")

 

    Select Case intA

        Case 0 To 9

            MsgBox "A er lille"

        Case 10 To 29

            MsgBox "A er under middel"

        Case 30 To 69

            MsgBox "A er middel"

        Case 70 To 89

            MsgBox "A er over middel"

        Case 90 To 100

            MsgBox "A er stor"

        Case Else

            MsgBox "Du skal taste tal mellem 0 og 100"

        End Select

    End Sub

 

Måske ikke så meget kortere, men efter min mening langt mere overskuelig, og i hvert flad noget nemmere at taste J.

 

Select Case kan anvende de samme sammenligningsoperatorer som If, foruden yderlige nogle stykker som fx a to b som brugt ovenfor.  Det er også muligt at teste på flere samtidige udfald, hvis disse skal have samme konsekvens:

 

    intTal = InputBox("Indtast tal mellem 0 og 9!")

 

    Select Case intTal

        Case 1, 3, 5, 7, 9

            MsgBox "Tallet er ulige"

        Case 2, 4, 6, 8, 0

            MsgBox "tallet er lige "

    End Select

 

- Til top -

 

Nested Selects

Også Select Case kan nestes ligesom de kan kombineres med If's, løkker og andet godt. I ovenstående eksempler, både  i If og Select Case, har jeg primært anvendt MsgBox'es i koden, men der kunne naturligvis have været alt muligt andet i stedet for. Eksempler på dette kan ses i nogle af mine kodeeksempler og der vil sikkert komme flere eksempler senere – også i disse artikler om programmering. Jeg vil afslutte denne artikel med en makro, der kombinerer nestede Select Case med If. Desuden vil jeg vise endnu et par indbyggede VBA funktioner.

 

Sub TestNavn()

    ' Først erklæres variable

    Dim strNavn As String

    Dim strFornavn As String

    Dim strEfternavn As String

    Dim strForAlfa As String

    Dim strEftAlfa As String

    Dim bytTest As Byte

    Dim bytTest2 As Byte

 

    ' Den første variabel tildeles en værdi via InputBox

    strNavn = UCase(InputBox("Indtast dit navn og Klik på OK"))

   

    'Det undersøges om inputboksen er udfyldt. Ellers stoppes udførelsen

    ' med en meddelelse til brugeren.

        If strNavn = "" Or InStr(strNavn, " ") = 0 Then

        MsgBox "Du skal indtaste dit navn, ellers virker funktionen ikke." _

            & " Prøv igen.", vbOKOnly + vbExclamation

        Exit Sub

    End If

   

    'Nu deles navnet i to ved brug af to andre erklærede variabler

    ' samt funktionerne Mid og InStrRev

   

    strFornavn = Mid(strNavn, 1, InStrRev(strNavn, " ") - 1)

    strEfternavn = Mid(strNavn, InStrRev(strNavn, " ") + 1)

    

    ' Og så er vi klar til at "lege" med Select Case

    ' Kan du gennemskue, hvad koden gør?

   

    Select Case strEfternavn

        Case "ABELSTEN" To "GREEN"

    ' Den følgende Select Case gentages identisk tre gange.

    ' Den kunne derfor med fordel laves som en selvstændig funktion

    ' som kaldes af de enkelte trin, men så får man ikke indtrykket

    ' af de nestede Seelcts.

            Select Case strFornavn

                Case "ABEL" To "GRY"

                    strForAlfa = "første"

                Case "HANNA" To "OTTO"

                    strForAlfa = "mellemste"

                Case "PALLE" To "ÅGE"

                    strForAlfa = "sidste"

                Case Else

                    bytTest = 1

            End Select

            strEftAlfa = "første"

        Case "HAAN" To "OZARK"

            Select Case strFornavn

                Case "ABEL" To "GRY"

                    strForAlfa = "første"

                Case "HANNA" To "OTTO"

                    strForAlfa = "mellemste"

                Case "PALLE" To "ÅGE"

                    strForAlfa = "sidste"

                Case Else

                    bytTest = 1

            End Select

            strEftAlfa = "mellemste"

        Case "PADGAARD" To "ÅSTRØM"

            Select Case strFornavn

                Case "ABEL" To "GRY"

                    strForAlfa = "første"

                Case "HANNA" To "OTTO"

                    strForAlfa = "mellemste"

                Case "PALLE" To "ÅGE"

                    strForAlfa = "sidste"

                Case Else

                    bytTest = 1

            End Select

            strEftAlfa = "sidste"

        Case Else

            bytTest2 = 1

            Select Case strFornavn

                Case "ABEL" To "GRY"

                    strForAlfa = "første"

                Case "HANNA" To "OTTO"

                    strForAlfa = "mellemste"

                Case "PALLE" To "ÅGE"

                    strForAlfa = "sidste"

                Case Else

                    bytTest = 1

            End Select

    End Select

 

    Select Case bytTest2

        Case Is = 0

            Select Case bytTest

                Case Is = 0

                    MsgBox "Dit fornavn ligger i den " & strForAlfa _

                        & " del af alfabetet, og dit efternavn i den " _

                        & strEftAlfa & " del", vbOKOnly + vbInformation

                Case Is = 1

                    MsgBox "Dit fornavn kunne desværre ikke testes, men " _

                        & "dit efternavn i den " & strEftAlfa _

                        & " sidste del af alfabetet", vbOKOnly + vbInformation

            End Select

        Case Is = 1

                    Select Case bytTest

                Case Is = 0

                    MsgBox "Dit fornavn ligger i den " & strForAlfa _

                        & " del af alfabetet, men dit efternavn kunne" _

                        & " desværre ikke testes", vbOKOnly + vbInformation

                Case Is = 1

                    MsgBox "Hverken dit fornavn eller efternavn kunne  " _

                        & "desværre testes. Måske skulle du opsøge en " _

                        & "numerolog?", vbOKOnly + vbInformation

            End Select

    End Select

   

End Sub

 

Koden tester først på om inputbox'en er udfyldt og udfyldt korrekt med både for- og efternavn. Hvis den ikke er, vil koden fejle senere, så derfor forlades koden. Her bruges en indbygget funktion, InStr til testen af om både fornavn og efternavn er udfyldt. InStr() finder en streng i en anden streng. Instr(strNavn, " ") betyder, at der skal ledes efter et mellemrum (" " ) i den værdi, der står i strNavn. Er resultatet 0, er det fordi, der ikke er et mellemrum, og så er boksen udfyldt forkert. Der vises en meddelelse og koden forlades.

 

Næste del af koden deler navnet i Fornavn og Efternavn og gemmer dem i relevante variable. Her bruges to nye indbyggede funktioner, Mid() og InStrRev(). InStrRev gør præcis det samme som InStr(), men bagfra. InStr() finder altså den første forekomst af et tegn, mens InStrRev () fiinder den sidste forekomst af et tegn. Eksempel InStr("Ole Emil Svendsen", " ") vil returnere 4, fordi det første mellemrum står på 4. posiition. InStrRev("Ole Emil Svendsen", " ") vil returnere 9 fordi det sidste mellemrum står på 9. position. Når man vælger at søge bagfra i denne sammenhæng er det fordi efternavnet ALTID er den absolut sidste del af navnet. Alle mellemnavne regnes med til fornavnet, også hvis man hedder Ole Emil Jensen Green. Her er fornavnet Ole Emil Jensen og efternavnet Green.

 

Den anden funktion, der anvendes er Mid(). Mid() finder værdien af en tekststreng fra en given position og et antal karakterer frem. Mid("Ole Svendsen", 1, 7) vil således returnere Ole Sve. Når man skal finde fornavnet, starter man "søgningen fra position 1 i strNavn. Vi finder det sidste mellemrum og trækker 1 fra dette for at få sidste bogstav i fornavnet. Når man skal hente efternavnet, starter man på positionen af sidste mellemrum og lægger 1 til, for at få første bogstav i efternavnet. Når der ikke er anført et antal tegn, der skal med, tager Mid() "resten" af strengen. Hvis vi ville være sikre, kunne vi have introduceret  endnu en indbygget funktion. Len(), der finder længden af en given streng.

 

strEfternavn = Mid(strNavn, InStrRev(strNavn, " ") + 1, Len(strNavn) _

    - InStrRev(strNavn, " ") + 1)

 

løser problemet, men det vil være lidt omstændeligt, især når det nu er unødvendigt. Nu kommer så vores første nestede Select Case. Den tester på værdien af efternavnet. Inden for de tre Case-områder, udfører vi så den samme Select Case, der tester på fornavnet. Findes fornavn eller efternavn ikke i de valgte områder, sættes en værdi til 1, ellers er den 0 (taldatatyper, der ikke tildeles en værdi får jo altid værdien 0). Når hele navneproblematikken er kørt gennem, skal vi så skrive til brugerne. Læg i øvrigt mærke til, at Case "Jensen" to Hansen" simpelthen returner Sand, hvis navnet alfabetisk ligger mellem Jensen og Hansen.

 

I den sidste Select Case tester vi så på om efternavnet kunne findes (bytTest2 = 0) eller ikke kunne findes (bytTest2 = 1) og inden for hver case udfører vi en tilsvarende test på fornavnet. Der returneres relevante meddelelser afhængig af de kombinerede værdier af bytTest og bytTest2.

 

Er det hele ikke bare særdeles spændende? Følg føljetonen i næste artikel, der allerede er på plads: "Løkker er lykken!"

 

- Til top -

- Tilbage til Programmering -
- Tilbage til makroer -
- Tilbage til Excel -