Tot nu toe bewandelen we onze code op een rechte weg. Op papier overlopen we ons Nassi Shneiderman diagram via proces blokken van boven naar onder. In de code maken we variabelen, bewerken ze met operaties en gebruiken ze in functies. Voeren we de code opnieuw uit, dan bewandelen we dezelfde weg. Misschien krijgen de variabelen andere waardes, geven de bewerkingen andere uitkomsten en retourneren onze functies andere output door gewijzigde argument waardes. Maar de weg er naar toe blijft steeds dezelfde.
In de oefeningen berekenden we de nulpunten van vierkantsvergelijkingen met behulp van de methode van de discriminant. Elke combinatie van waardes voor a, b en c uit een gegeven vierkantsvergelijking geeft een uniek paar nulpunten. Toch is er een probleem. Voor bepaalde combinaties a, b en c wordt de discriminant negatief. Voor de computer reden genoeg om er de brui aan te geven - we krijgen een foutmelding, want een vierkantswortel van een negatief getal bestaat niet. Uit de cursus wiskunde weten we echter al dat een negatieve discriminant geen reden is tot paniek. Het betekent eenvoudigweg dat er geen nulpunten bestaan. Een oefening op papier kunnen we dus met een gerust hart beëindigen en schrijven op dat er voor de gegeven vierkantsvergelijking geen nulpunten bestaan.
Ipython kan deze redenering (‘Als de discriminant kleiner is dan nul, dan hoef je geen nulpunten te berekenen.’) niet uit zichzelf maken.
We moeten zélf aangeven dat, afhankelijk van het teken van de discriminant, een ander stuk code moet worden uitgevoerd.
De tot-nu-toe rechte weg splitst op: de ene weg berekent verder de nulpunten.
Veilig, want we slaan enkel die weg in als de discriminant groter dan of gelijk is aan nul.
De andere weg stopt de berekening en geeft aan (print()
) dat er geen nulpunten zijn.
Soms splitst ons algoritme in verschillende wegen. Op papier verschijnt in het Nassi Shneiderman diagram een selectieblok. In code hebben we nood aan “conditionele statements” of kortweg “condities” genoemd, ingebouwd in de syntax van elke programmeertaal. Voor we in detail naar condities kijken, moeten we leren over het boolean datatype en boolean expressies.
Boolean is het vierde datatype dat in deze cursus aan bod komt, na integer, float en string (of het vijfde voor wie ook list heeft onthouden).
Een boolean is ofwel True
ofwel False
(Merk op dat Python hoofdlettergevoelig is. True
en False
zijn verplicht altijd met hoofdletter geschreven!)
In Python wordt “waar” weergegeven door de waarde True
, en “onwaar” door de waarde False
.
True
en False
zijn de enige booleans;
alles wat niet False
is, is automatisch True
(er bestaat geen ‘misschien’).
Moeten we echt speciaal een nieuw datatype introduceren hiervoor? Wel, ja, ze zijn cruciaal wanneer we willen waardes of variabelen met elkaar vergelijken door middel van boolean expressies.
Eerder definiëerden we een expressie (of berekening) als een combinatie van één of meerdere waardes (zoals strings, integers, of floats) met behulp van operatoren, die een nieuwe waarde opleveren.
Zo beschouwden we de expressie 2+3 die de integer 5 geeft, of “Appel”+”Peer” die de string “AppelPeer” oplevert.
Een boolean expressie levert een boolean waarde (dus True
of False
) op door middel van vergelijkingsoperatoren, logische operatoren en/of de in-operator.
Vergelijkingsoperatoren zijn de operatoren die strings, integers of floats vergelijken om uiteindelijk een boolean waarde op te leveren:
Symbool | Betekenis |
---|---|
< |
kleiner dan |
<= |
kleiner dan of gelijk aan |
== |
gelijk aan |
>= |
groter dan of gelijk aan |
> |
groter dan |
!= |
niet gelijk aan |
De uitkomst na een vergelijking is True
of False
.
2<5 geeft logischerwijs True
, 2>=5 geeft False
.
Een zeer vaak gemaakte fout is om twee waardes te vergelijken met een enkel gelijkheidsteken.
Echter, de enkele = is de assignment operator, nodig om variabelen onder een naam op te slaan, géén vergelijkingsoperator.
Gelukkig produceert Python meestal meteen een foutmelding als je de = probeert te gebruiken in plaats van == om twee waardes te vergelijken.
Je kunt deze vergelijksoperatoren ook gebruiken tussen strings.
Vergelijkingen tussen strings zijn alfabetische vergelijkingen,
waarbij je wel moet bedenken dat hoofdletters als kleiner beschouwd worden dan kleine letters en cijfers als kleiner dan alle letters.
Zo kun je nagaan dat elk van onderstaande vergelijkingen True
geven:
print( 2 < 5 )
print( 2 <= 5 )
print( 3 >= 3 )
print( 3 == 3.0 )
print( 3 != "3" )
print( "syntax" == "syntax" )
print( "Python" != "rotzooi" )
print( "Python" > "Perl" )
print( "banaan" < "mango" )
Logische operatoren zijn operatoren die boolean waardes met elkaar vergelijken.
Er zijn drie logische operatoren: and
, or
, en not
.
Een voorbeeld van het gebruik van logische operatoren binnen boolean expressies is:
print( (2<5) and (10>=3) )
Deze expressie levert True
, want zowel (2<5) als (10>=3) zijn True
, en True and True
geeft opnieuw True
.
Stellen we in het algemeen even de boolean expressie (2<5) voor door A en (10>=3) door B, dan geldt:
A | B | A and B |
---|---|---|
False | False | False |
False | True | False |
True | False | False |
True | True | True |
A | B | A or B |
---|---|---|
False | False | False |
False | True | True |
True | False | True |
True | True | True |
A | not A |
---|---|
False | True |
True | False |
Python heeft een speciale operator die de “lidmaatschap test operator” heet,
en die vanwege die onverkwikkelijke mondvol meestal kortweg de in
operator wordt genoemd.
De in
operator test als een string waarde voorkomt in een string, of als een element voorkomt in een list.
Voor de string test kun je testen of een specifiek teken of een groepje tekens, onderdeel is van een string.
Is het antwoord ‘ja’, dan geeft Python True
, anders False
.
Volgende voorbeelden geven allemaal True
:
print( "t" in "Python" )
print( "ytho" in "Python" )
print( "5" in "0123456789" )
print( 5 in [4,6,5,9] )
print( "appel" in ["banaan","appel","peer","druif"] )
Zo, we weten wat booleans zijn en hoe we ze binnen boolean expressies gebruiken. Laat mij toe om hun nut in de verf te zetten.
Booleaanse expressies zijn zeer handig om een actie te laten afhangen of een bepaalde uitspraak wel of niet waar is.
Voor we dit in Python zien, eerst een voorbeeld uit het dagelijkse leven.
“Als de vuilzak vol is, dan zetten we die buiten.”
Wel, misschien heb je hierbij net als ik al een bedenking?
Je zet die natuurlijk niet buiten op een dag dat het huisvuil niet wordt opgehaald.
Daarom kunnen we beter nog wat specifieker zijn, met behulp van de logische operator and
:
“Als de vuilzak vol is and het huisvuil wordt morgenochtend opgehaald, dan zetten we de vuilzak buiten.”
Let vooral op de kernwoorden als en dan.
We onderzoeken of een bewering al dan niet klopt,
en afhankelijk daarvan beslissen we om een actie wel of niet uit te voeren.
Terug naar onze splitsing van de weg.
Stel dat we de tekst “De discriminant is kleiner dan nul!” willen laten verschijnen als de discriminant negatief is.
Dit kunnen we inbouwen met behulp van het gereserveerde Python woord if
.
De juiste syntax kunnen we best via dit heel eenvoudig voorbeeld bekijken:
D = -5 # normaal resulaat van berekening ipv manueel gedefinieerd
if D < 0:
print( "De discriminant is kleiner dan nul!" )
Nadat we in de eerste lijn de discriminant manueel een waarde hebben gegeven (in oefeningen zul je deze zelf berekenen), springen drie zaken in het oog:
if
. Daarna volgt de conditie waaraan moet voldaan zijn om verder te gaan. Deze conditie is veelal een boolean expressie die als resultaat True
of False
geeft. In bovenstaand voorbeeld wordt nagegaan of de discriminant D kleiner is dan nul. Omdat we manueel de variabele D gelijk gesteld hebben aan de waarde -5, zal deze boolean expressie dus True
opleveren.True
geeft. Was de conditie False
, dan worden de ingesprongen lijnen genegeerd.We vatten deze syntax regels kort als volgt samen:
if boolean expressie:
acties
waarbij de boolean expressie en acties worden ingevuld afhankelijk van de context van het probleem. Hieronder volgt nog een tweede voorbeeldje, waarbij we testen of de waarde van een variabele (dewelke we eerst manueel definiëren) gelijk is aan vijf:
x = 5 # normaal resulaat van berekening ipv manueel gedefinieerd
if x == 5:
print( "Deze lijn wordt alleen uitgevoerd als x gelijk aan 5" )
print( "Let vooral op de insprong van deze drie lijnen!" )
print( "Ook deze lijn wordt enkel uitgevoerd als x gelijk aan 5." )
print( "Deze lijn is niet ingesprongen en wordt sowieso uitgevoerd." )
Let hierbij ook op het cruciale verschil tussen de assignment x=5
en de boolean expressie x==5
op de eerste en tweede lijn. De assignment slaat de waarde 5 op in de variabele x (of naar analogie met de doos: we steken de integer 5 in de doos waarop we de naam x schrijven), de boolean expressie vergelijkt of de waarde van x gelijk is aan 5 (we kijken als het ware in de doos en controleren of de waarde in de doos gelijk is aan 5). Wat zou de uitkomst van bovenstaande code zijn wanneer we x=6
stellen op de eerste lijn?
Zo, de syntax is hopelijk duidelijk. Toch wil ik nog even het belang van de insprong benadrukken. Zonder correcte insprong kan Python niet zien welke regels code een “actie blok” vormen, en kan daarom niet je code uitvoeren zoals jij dit wilt. Je kunt inspringen door middel van de Tab toets, of je kunt het doen middels spaties. De meeste editors doen aan “auto-indenting”, dat wil zeggen, ze springen automatisch in als de code daartoe aanleiding geeft. Zo zal Jupyter Notebook bij het schrijven van het if boolean expressie:
statement automatisch de daarop volgende regel meteen laten inspringen. Wanneer dat niet gebeurt, heb je waarschijnlijk een syntax fout gemaakt, bijvoorbeeld de dubbele punt vergeten. Ook wordt het niveau van inspringing aangehouden voor iedere volgende regel, tenzij je middels de Backspace toets inspringing verwijdert. In Python programma’s is inspringing normaal vier spaties, dus een druk op de Tab toets moet vier posities inspringen. Zolang je in een specifieke editor werkt, kun je ofwel de Tab toets gebruiken, ofwel zelf de spatiebalk vier keer indrukken, om één niveau van inspringing op te schuiven.
In veel programmeertalen (of eigenlijk in vrijwel alle programmeertalen) worden blokken code herkend doordat ze beginnen en eindigen met een speciaal symbool of gereserveerd woord. Bijvoorbeeld, in talen als Java en C++ worden blokken code omsloten door accolades, terwijl in talen als Pascal en Modula ze beginnen met het woord begin en eindigen met het woord end. Dat betekent dat in vrijwel alle talen correcte inspringing niet nodig is. Toch zie je dat ervaren programmeurs altijd correct inspringen, ongeacht de taal die ze gebruiken. Dat maakt het namelijk gemakkelijk te zien welke delen van de code bij elkaar horen, bijvoorbeeld, wat er hoort bij een if
statement. Bij Python is het inspringen een verplichting. Voor ervaren programmeurs die voor het eerst Python leren komt dat wat vreemd over, maar ze beseffen al snel dat het ze niks uitmaakt – ze lieten hun code toch al netjes inspringen. Python maakt het echter ook voor beginnende programmeurs een noodzakelijkheid om netjes in te springen, wat betekent dat ze gedwongen zijn om nette code te schrijven. En dat is alleen maar nuttig voor iedereen.
Veelal willen we niet enkel actie ondernemen wanneer een conditie True
is. Als we terugdenken aan het voorbeeld van de discriminant, willen we een andere actie ondernemen als D<0 niét geldt. Dit noemen we de twee-weg beslissing. Als de conditie True
geeft, kiezen we de ene weg, anders (de conditie is dan False
) gaan we een andere weg op. De Python syntax hiervoor bevat de gereserveerde Python woorden if
en else
. Laat ons er meteen opnieuw een voorbeeldje bijnemen:
D = 5 # normaal resulaat van berekening ipv manueel gedefinieerd
if D < 0:
print( "De discriminant is kleiner dan nul!" )
else:
print( "De discriminant is groter dan of gelijk aan nul," )
print( "binnen deze indented blok kunnen we nulpunten berekenen." )
Bij de twee-weg beslissing wordt de tweede weg ingeleid door de else:
meteen gevolgd door een dubbelpunt. De auto-indenting van Jupyter Notebook genereert hierna automatisch een nieuwe ingesprongen blok. Hierin komen alle acties die we willen uitvoeren wanneer de conditie D<0 niét geldt (en dus False
geeft).
Ook hier kunnen we de syntax regels kort samenvatten:
if boolean expressie:
actie1
else:
actie2
Het is van belang dat het woord else
uitgelijnd is met het woord if
waar het bij hoort. Als
je ze niet correct uitlijnt, krijg je ofwel meteen een foutmelding ofwel onverwachte resultaten.
Een gevolg van het toevoegen van een else
tak aan een if
statement is dat altijd precies één van de twee blokken acties wordt uitgevoerd. Als de boolean expressie True
is, wordt actie1 uitgevoerd, en wordt actie2 overgeslagen. Als de boolean expressie False
is, wordt actie1 overgeslagen, maar wordt actie2 uitgevoerd.
Soms komt onze weg op kruispunten met meer dan twee uitwegen. Dat wordt duidelijk aan de hand van volgend voorbeeld, waarin onze code een tekst print afhankelijk van de leeftijd die aan de gebruiker wordt gevraagd.
leeftijd = int( input('Hoe oud ben je? ') )
if leeftijd < 12:
print( "Je bent nog een kind." )
elif leeftijd < 18:
print( "Je bent een tiener." )
else:
print( "Volwassen!" )
Tussen het if
en else
statement hebben we een extra stuk toegevoegd, ingeleid door een nieuw gereserveerd woord elif
. De syntax is in het algemeen als volgt:
if boolean expressie1:
actie1
elif boolean expressie2:
actie2
else:
actie3
De verschillende boolean expressies worden door een if-elif-else constructie in volgorde gecontroleerd. De boolean expressie1 wordt als eerste gecontroleerd. Als die True
geeft, wordt het bijbehorende blok code actie1 uitgevoerd. Geen andere blokken code in de hele constructie worden daarna nog uitgevoerd, en er worden daarna ook geen boolean expressies in de constructie meer getest.
Daarentegen, als boolean expressie1 False
geeft, dan wordt de actie1 blok overgeslagen en wordt boolean expressie2 horend bij de elif
gecontroleerd. Is deze True
, wordt het bijbehorende blok code actie2 uitgevoerd. Enkel als ook deze boolean expressie False
geeft, komen we bij actie3 horend bij de else
blok.
In zowel het voorbeeld als de algemene syntax samenvatting hebben we een drie-weg beslissing gebouwd. Echter, door meerdere elif
statements toe te voegen, kunnen we een constructie met een willekeurig aantal mogelijkheden maken.