De bouwstenen voor data manipulatie

Calculatie hebben we al gedaan, maar als samenvatting nog even de eenvoudige bewerkingen die je na deze introductie workshop al kunt uitvoeren.

Teken Betekenis
+ Optellen
- Aftrekken
* Vermenigvuldigen
/ Delen

Combineer dit met haakjes om de juiste volgorde van de bewerkingen te selecteren, en je kunt al veel doen. Zoals je eerder gezien hebt, kun je deze bewerkingen uitvoeren met de getallen zelf, of met variabelen (danwel constanten) die een getal als waarde hebben.

Dan wordt het nu tijd voor de andere twee vormen van data manipulatie. In het begin van dit document heb ik ze omschreven, nu ga ik de Engelstalige termen gebruiken waaronder je ze meestal tegenkomt. Het gaat om conditional statements en repetitive statements. Kun je ze zelf relateren aan de omschrijvingen die ik eerder gaf? We gaan ze nu in de praktijk verkennen!

Conditional statements

Een conditie is een voorwaarde waaraan gegevens kunnen voldoen. De algemene vorm van een conditional statement is de volgende: als aan een bepaalde voorwaarde wordt voldaan dan doe dit, anders doe dat. In het Engels worden conditional statements daarom ook wel aangeduid als if-else statements. Een eerste voorbeeld zag je al in het laatste code-voorbeeld: als er sprake is van een fracuur, dan moet ’ie gerepareerd (volgens de orthopeed). De else is dus niet verplicht.

// If-else with Boolean value
var patientHasOverweight = false
var advise = ""

if patientHasOverweight {
    advise = "weight reduction is indicated"
} else {
    advise = "keep you weight on this level"
}

print("Your personal advise is: \(advise)")

Je ziet dat voor advise (binnen het if-else statement) automatisch wat extra witruimte wordt geplaatst. Dat zijn een bepaald aantal spaties (meestal 2 of 4), die je netjes handmatig kunt plaatsen middels de TAB toets die links bovenaan je toetsenbord zit (meest linker toets onder de regel met de cijfers). Deze hebben bij de meeste programmeertalen geen functie, ze dienen zuiver om de leesbaarheid te bevorderen. Je ziet dan aan het inspringingsniveau welke code bij elkaar hoort.

Nu zijn Boolean values handig, maar je kunt ook met tekst en getallen werken. Daarbij kun je de volgende vergelijkingen uitvoeren:

Vergelijking Betekenis
== Is gelijk aan
!= Is ongelijk aan
> Is groter dan
>= Is groter dan of gelijk aan
< Is kleiner dan
<= Is kleiner dan of gelijk aan

Zo ziet dat met tekst uit:

// If-else with text
var hemisphere = "left"
var bodySide = ""

if hemisphere == "left" {
    bodySide = "right"
} else {
    bodySide = "left"
}

print("The \(hemisphere) motor cortex provides innervation for the \(bodySide) arm and leg.")

Nu zie je gelijk een bron van verwarring en mogelijke fouten. Misschien heb je die fout zelfs gemaakt net, en dat is mede een reden waarom je deze code zelf moet intypen. Om twee waarden te vergelijken op gelijkheid gebruik je == maar om een waarde toe te kennen aan een variabele (of constante) gebruik je =. Als je = zou gebruiken binnen een vergelijking, krijg je een foutmelding (probeer maar!).

// If-else with numbers
var currentSystolicPressure = 120
var previousSystolicPressure = 160

if currentSystolicPressure < previousSystolicPressure {
    print("Blood pressure has decreased.")
}

Je kunt voorwaarden ook combineren met de volgende symbolen:

Symbool Betekenis
&& De ene voorwaarde en de andere
|| De ene voorwaarde of de andere

Het kan handig zijn om met haakjes te werken, net als bij calculaties, om zeker te zijn dat voorwaarden op de juiste manier worden gecombineerd.

// If-else combinations 
// Example based on SIRS criteria (Systemic Inflammatory Response Syndrome)

var temperatureInCelcius = 38.0
var pulse = 100
var ventilationFrequency = 18
var leukocytesInBillion = 10
var infectionSuspected = true
var twoOrMoreSIRSCriteriaPresent = false

if (temperatureInCelcius > 38.3 || temperatureInCelcius < 36) || pulse > 90 || ventilationFrequency > 20 || leukocytesInBillion > 12 {

    twoOrMoreSIRSCriteriaPresent = true
}

if twoOrMoreSIRSCriteriaPresent && infectionSuspected {
    print("Sepsis criteria are met according to SIRS.")
}

Bovenstaand voorbeeld maakt gebruik van de SIRS criteria voor sepsis. De haakjes rondom de temperatuur-criteria staan er voor mezelf, het zal voor de computer geen verschil maken omdat het alleen maar || vergelijkingen betreft. Je kunt && en || combineren in een statement, ik heb ze in bovenstaand voorbeeld apart gelaten om het statement leesbaar te houden. De computer maakt geen fouten, maar als ik een fout maak in de code omdat ik het zelf niet meer begrijp door alle haakjes en combinaties, dan zal de uitkomst ook fouten kunnen bevatten. En dit soort fouten zal XCode niet ontdekken voor ons….

De ironie wil dat in bovenstaand voorbeeld een fout zit, die tijdens een van de workshops ontdekt werd. Ik laat hem bewust staan en daag je uit een oplossing te vinden. Hint: het is een fout in de logica, niet in de syntax. De compiler van XCode zal niet klagen, maar (als je dit in een app plaatst) de eindgebruiker wel. Die krijgt in sommige gevallen namelijk een foutief advies.

Verder is het je misschien opgevallen dat ik alleen var gebruik heb in plaats van let. Binnen de code-voorbeelden had ik evengoed (of zelfs beter) let kunnen gebruiken, maar het idee is dat de input voor dit soort vergelijkingen straks van de gebruiker komt, en niet “hardcoded” in het programma zit verwerkt. Daarop vooruitlopend heb ik er var van gemaakt: het heeft weinig zin om de gebruiker om input te vragen als de waarde nadien niet mag veranderen, toch?

Repetitive statements

Als ik jou honderd strafregels laat schrijven, ben je niet blij. De computer maakt het niet uit. Eén regel, 10, 100 of 10.000 - piece of cake! Dat doen we met repetitive statements, ook wel loops genoemd (een “lus” die je een x-aantal keren herhaalt). Er zijn twee soorten loops, de for-loop* en de while-loop**. In deze intro behandel ik alleen de for-loop, die zul je in de praktijk ook het meest gebruiken (om eerlijk te zijn, gebruik ik de while-loop bijna nooit).

Typ (ja, nog steeds!):

// Simple for-loop
let max = 5

for i in 1...max {
    print("Line number \(i)")
}

Wat deze code doet, is het volgende: voor (for) elke herhaling met nummer i, startend bij 1 en eindigend bij max, voer de code uit die tussen {} staat. Ik heb bewust i gekozen omdat dit een gebruikelijke parameter is binnen loops, maar je mag kiezen wat je wil. De syntax 1...max betekent dat de waarde van max nog wordt gebruikt in de loop. Als je dit niet wilt, dus tot de waarde van max maar niet verder, kun je 1..<max gebruiken.

Om het resultaat te zien, moet je een paar keer klikken. Beweeg met de muis over de tekst (5 times) links in de grijze kolom en identificeer het open rondje dat dan helemaal rechts op die regel verschijnt:

For-loop 01

Je krijgt dan het volgende te zien:

For-loop 02

Klik nu met de rechter muisknop op het grijze vakje waar “Line number 5” te zien is, en selecteer de optie Value history en je krijgt alle waarden te zien:

For-loop 03

Een andere benadering is gebruik maken van de console. Zoek rechtsboven in je venster het vierkantje op met een horizontale streep en selecteer deze:

For-loop alternative 01

Beneden in beeld opent zich dan een extra venster, en dit geeft (o.a.) de output van je loop weer:

For-loop alternative 02
Arrays

Tot nu toe hebben we alleen variabelen / constanten gebruikt die één waarde tegelijk konden bevatten. De waarde kan weliswaar wisselen, er zijn verschillende soorten waarden, maar er is slechts een waarde tegelijk. Er zijn ook variabelen (of constanten) die meerdere variabelen tegelijk kunnen bevatten. Degene die we hier introduceren is de meest gebruikte, de array. Een array bevat altijd één soort waarden, maar kan meerdere waarden bevatten. Tijd voor een voorbeeld:

// Array example
let cerebralLobes = ["frontal", "parietal", "temporal", "occipital"]

for lobe in cerebralLobes {
    print("One of the four cerebral lobes is the \(lobe) lobe.")
}

De array cerebralLobes bevat een viertal waarden, elk bestaand uit tekst (een string dus). De individuele waarden staan tussen aanhalingstekens, net als bij een losse waarden. De gehele collectie staat tussen [] en individuele waarden worden gescheiden door een komma. In het voorbeeld zie je hoe je een array door kunt gaan middels een loop. Op deze manier wordt het al handiger.

Nu, stel dat je van 10 personen de Body Mass Index moet berekenen. Je kunt tien keer de formule voluit schrijven en de juiste waarden voor gewicht en lengte invullen. Maar je kunt het ook zo doen:

// Another array example
// Weight and length of 8 persons
// Assume data are in the same order

let weightInKilogram = [67, 73, 90, 59, 77, 83, 75, 79]
let lengthInCentimeters = [180, 175, 177, 160, 170, 169, 179, 190]
let totalPersons = 8

for i in 0..<totalPersons {
    let lengthInMeters = Double(lengthInCentimeters[i]) / 100.0
    let bmi = Double(weightInKilogram[i]) / (lengthInMeters * lengthInMeters)
    print("BMI for person \(i+1) is \(bmi).")
}

Oke, hier gebeurt veel. We gaan er stap voor stap doorheen. Ik heb twee arrays gemaakt, eentje met gewicht en eentje met lengte. De volgorde van de waarden in beide arrays is gelijk (persoon1, persoon2, etc). Nu zou je kunnen zeggen for weight in weightInKilogram {} maar dan kom je niet uit met lengte. En vice versa. Wat we hier als oplossing gebruiken, is de waarden van de array individueel benaderen. In Swift zijn arrays zero-indexed, net als in de meeste programmeertalen. Dat betekent dat je de eerste waarde van weightInKilogram kunt aanroepen met weightInKilogram[0]. In totaal zijn er 8 personen, de laatste waarde is dus weightInKilogram[7] omdat we bij nul beginnen tellen. Dan snap je waarom ik kies voor for i in 0..<totalPersons {}. In de volgende regel wil ik de lengte in meters hebben (voor de verandering heb ik de brondata hier in centimeters gedaan) en dus moet ik elke waarde delen door 100. Omdat XCode een melding gaf dat de typen “int” (de lengte in cm is een integer) en “Double” (100.0) niet overeenkwamen, en ik per se een Double wil hebben (geen integer) om ook de decimalen te kunnen meenemen, zet ik de waarde handmatig over middels Double(...). De rest van de code spreekt dan voor zich, waarbij ik person \(i+1) moet doen om de persoon wel bij 1 te laten beginnen.

Nu heb ik zelf een commentaar op bovenstaande code. Door let totalPersons = 8 te doen, heb ik de lengte van de array een harde waarde gegeven. Wat als ik besluit in beide arrays een persoon weg te halen of toe te voegen? Dan moet ik er ook aan denken dit getal aan te passen, anders ontstaat een foutmelding. Een betere oplossing is deze:

let totalPersons = weightInKilogram.count

Met .count tel je het aantal waarden in een array, en nu wordt dit automatisch aangepast. Als je het helemaal mooi wil doen, is dit een optie:

// Same example with error handling
let weightInKilogram = [67, 73, 90, 59, 77, 83, 75, 79]
let lengthInCentimeters = [180, 175, 177, 160, 170, 169, 179, 190]
var totalPersons = 0

if weightInKilogram.count == lengthInCentimeters.count {
    totalPersons = weightInKilogram.count
} else {
    print ("Both arrays have a different length.")
}

for i in 0..<totalPersons {
    let lengthInMeters = Double(lengthInCentimeters[i]) / 100.0
    let bmi = Double(weightInKilogram[i]) / (lengthInMeters * lengthInMeters)
    print("BMI for person \(i+1) is \(bmi).")
}

Eerst stel je totalPersons in op 0, in dat geval zal de loop 0 keren worden uitgevoerd. Daarna check je of de lengte van beide arrays gelijk zijn, en in dat geval zal de loop worden uitgevoerd. Anders volgt er een nette foutmelding.

One more thing… de reden waarom ik var totalPersons = 0 al aan het begin instel, en niet pas binnen het if-else statement, heeft te maken met de scope van een variabele. Hoe meer “naar binnen” (in een loop, statement, functie) je deze declareert, des te minder zichtbaar is ’ie daarbuiten. Probeer maar eens te veranderen in:

// Error producing example
let weightInKilogram = [67, 73, 90, 59, 77, 83, 75, 79]
let lengthInCentimeters = [180, 175, 177, 160, 170, 169, 179, 190]

if weightInKilogram.count == lengthInCentimeters.count {
    let totalPersons = weightInKilogram.count
} else {
    print("Both arrays have a different length.")
}

for i in 0..<totalPersons {
    let lengthInMeters = Double(lengthInCentimeters[i]) / 100.0
    let bmi = Double(weightInKilogram[i]) / (lengthInMeters * lengthInMeters)
    print("BMI for person \(i+1) is \(bmi).")
}

XCode meldt nu “Use of unresolved identifier ‘totalPersons’ ” omdat de waarde nu alleen bestaat binnen het if-else statement en niet daarbuiten.

Samenvatting

Je hebt leren werken met XCode en playgrounds, je hebt de basis van programmeren geleerd in Swift, je kunt nu werken met variabelen en constanten, zelfs arrays die meerdere waarden kunnen bevatten, en je kunt data manipulatie uitvoeren in de vorm van berekeningen, conditional statements en repetitive statements. Dat lijkt me meer dan voldoende voor deze eerste dag!

Morgen gaan we XCode verder verkennen en een tweetal apps bouwen!