2.2 Goochelen met code

Voor de meeste mensen hangt er een waas van mysterie en magie rond software en hardware. Het lijkt allemaal ontzettend ingewikkeld. Een leek die voor het eerst de achterliggende programma-instructies bekijkt, voelt zijn hoofd duizelen. Een programma betekent letterlijk een “programma”, in de simpelste vorm een reeks instructies die je na elkaar moet voltooien. Je kan niet zonder meer de volgorde veranderen, net zoals je bij een recept een reeks instructies na elkaar moet volgen om het gewenste eindresultaat te bereiken. Anders zal je pudding of chocoladecake er niet zo netjes uitzien en wellicht ook niet eetbaar zijn. Een computerprogramma gaat vaak verder dan een simpele reeks instructies. Het stuk hardware (computer, microcontroller, smartphone...) moet het programma zelfstandig (eventueel met interactie van de gebruiker) kunnen afhandelen. Het programma staat er alleen voor. Pudding zal niet protesteren als de kinderen met hun lepels pudding naar elkaar beginnen gooien. Maar hardware is nu eenmaal niet zo “intelligent” als een kind, het beseft niet altijd wat gebruikers beginnen doen. Misschien doet de gebruiker wel dingen die de programmeur niet bedoeld heeft. Daarom moet de programmeur rekening houden met (bijna alle) denkbare “wat als”-scenario's.

2.2.1 Binaire code en programmeertalen

Menselijke talen zijn veel te complex om dienst te doen als programmeertaal. Programmeertalen moeten zo zijn opgebouwd dat elke instructie maar één ding kan betekenen. Het is voor een computer “zus” (0 of 1) of “zo” (1 of 0). Je weet wel: binaire code. Dit komt omdat de huidige computersystemen niet meer zijn dan een gigantisch grote hoeveelheid schakelaars. Zo'n beetje zoals de schakelaar waarmee je het licht aan en uit schakelt, maar dan vele malen kleiner.

Nullen en enen in een matrix om een afbeelding te tekenen (javascriptcode).

Omdat het omgekeerd voor een mens behoorlijk moeilijk is om pakweg tien miljoen schakelaars te bedienen enkel door een reeks nullen en enen in te tikken, zijn er oplossingen bedacht in de vorm van programmeertalen. Een programmeertaal bevat wel woorden uit menselijke talen (meestal uit het Engels), waardoor ze bevattelijk wordt. Meestal gaat het om korte instructies, mnemotechnische hulpmiddelen, symbolen, operatoren of logische schakelingen zoals and, or, if, else, end, print ... Omdat die taal niet uit binaire code (nullen en enen) bestaat, is ze voor computers niet begrijpelijk.



Herkenbare woorden in programmeercode.

Een compiler is een speciaal stukje software (weer een programma) dat de in een programmeertaal geschreven instructies omzet in binaire code of machinetaal. Zo blijft de code voor de programmeur leesbaar en kunnen de instructies nadien ook weer aangepast worden. Na elke aanpassing aan de code moet het programma weliswaar opnieuw worden gecompileerd.

Omdat het programmeren in dit soort assembleertaal toch nog heel wat inzicht vroeg, zijn later de zogenaamde ‘hogere programmeertalen’ en ‘scriptingtalen’ ontwikkeld. Ze staan een stuk dichter bij de menselijke taal en zijn hierdoor eenvoudiger te leren. In de loop der jaren (en nog steeds) zijn er honderden verschillende programmeertalen bedacht, vaak met een specifiek doel voor ogen. Elke programmeertaal moet echter op één of andere manier “verwerkt” worden vooraleer het een bruikbaar programma is.

In de programmeertaal Processing tovert de instructie ellipse(20, 20, 20, 10); een cirkel op het scherm. Het middelpunt van de cirkel ligt op pixel 20 van de x-as en pixel 20 van de y-as te rekenen vanuit de linkerbovenhoek van het uiteindelijke programma (dus niet vanuit de rechterbenedenhoek zoals bij een grafiek in bijvoorbeeld MS Excel). De cirkel is 20 pixels breed en 10 pixels hoog... geen echte cirkel dus, maar een ellips.

Voor het bouwen van gebruiksvriendelijke programma’s met een aangename interface zijn heel visuele programmeeromgevingen gebouwd, waarbij je de code niet altijd meer moet kennen. Zo sleep je bij de programmeeromgeving voor de Lego® Mindstorms-robots de code in visuele blokjes bij elkaar op een computerscherm.

2.2.2 Omzetten van programmeercode naar binaire code

De manier waarop computers programmeercode verwerken kan verschillen:

Soort verwerking

Uitleg

Voorbeelden van software

Compilers

De meeste programma's worden gecompileerd . Een “compiler” is een stuk software dat de codes vertaalt naar nullen en enen. Omdat niet elk besturingssysteem en/of processor op dezelfde manier met nullen en enen omgaat, heb je voor elk benodigd platform een andere compiler nodig. Er bestaan niet voor alle programmeertalen compilers voor alle platformen. Microsoft bouwt voor zijn programmeertalen geen compilers voor pakweg Linux of Mac OS X.

Een compiler maakt het werk van de programmeur dus niet op alle vlakken eenvoudiger. Hij moet bezinnen voor hij begint! Gecompileerde software is zeer snel omdat het eindresultaat uit nullen en enen bestaat, de taal van de processor zelf. De code is echter niet meer leesbaar, tenzij de programmeur de code ook publiceert (zoals bij open source het geval is).

Besturingssystemen zoals Windows, Mac OS X, Linux, MS Office, OpenOffice...

Interpreters

Soms is een stuk code erg beperkt en kan die worden gecompileerd wanneer de gebruiker het programma opent. De code wordt op dat moment “geïnterpreteerd”, simultaan vertaald naar processorinstructies en uitgevoerd. De bekendste interpretertaal is javascript. Een webontwikkelaar kan javascript-code in zijn webpagina's opslaan. Wanneer de browser van een websitebezoeker de pagina opent, wordt de leesbare code door de browser “geïnterpreteerd” en (interactief) uitgevoerd.

Een browser (Safari, Chrome, Internet Explorer, Opera, Firefox...) beschikt zo goed als altijd over een “javascript-engine” die de code in webpagina's kan interpreteren en uitvoeren.

Vermits je enkel een “browser” nodig hebt om het programma uit te voeren, werkt javascript doorgaans op alle platformen en systemen waarvoor een browser bestaat.

Javascript in webpagina's, javascriptbibliotheken zoals jQuery...

bytecode

Bytecode bevindt zich ergens tussen compilers en interpreters in... Het lijkt een beetje op “interpreter”software omdat de code niet volledig wordt gecompileerd. Het heeft een extern programma nodig om “gestart” te kunnen worden.

Toch verschillen bytecode-programma's van scriptingtalen zoals javascript omdat de code gedeeltelijk wordt gecompileerd naar een tussenliggende taal, “bytecode”.

Java is een bekend voorbeeld. Om Java-programma's te starten, heb je een “runtime environment” nodig. Zo'n runtime environment moet je eerst op je computersysteem of hardware installeren. Wanneer je het java-programma start, wordt op de achtergrond de “runtime environment” (de zogenaamde JRE) gestart.

Wanneer de programmeur klaar is met de code, moet hij die exporteren. De code wordt op dat moment vertaald naar “bytecode” (geen nullen en enen) die door de “runtime environment” wordt uitgevoerd.

Het grote voordeel is dat de programmeur slechts één keer code moet schrijven voor alle platformen. Java-programma's werken op alle platformen waarvoor een JRE bestaat, Flash werkt op alle platformen waarvoor een Flashplayer bestaat. Bytecode-programma's zijn wel trager dan gecompileerde programma's.

Java (jar-bestanden) en de Java Runtime Environment

Flash (swf-bestanden) met als “uitvoerprogramma's: de Flashplayer (voor browsers) en Adobe Air (voor desktopomgevingen).

2.2.3 Functies en methodes

Vergelijk een programma of applicatie met een auto. De buitenkant van de auto en vooral het dashboard, stuur, pedalen, handrem, versnellingspook vormen de interface, waarmee de chauffeur de auto bestuurt. Onder de motorkap zit de motor die reageert op de acties van de chauffeur. Een programmeertaal zelf is nog geen motor en al zeker geen auto. Met een programmeertaal beschik je wel over een manier en de onderdelen om een auto met motor te bouwen. De programmeur is tegelijk de ingenieur en de monteur die de motor ontwikkelt en bouwt. Niet alle programmeurs zijn daarin even bedreven. Daarom heb je soms een uitzonderlijk goed programma op het vlak van functionaliteit, maar een vreselijke bedieningsinterface. Ook het omgekeerde kan het geval zijn. Uiteraard werken programmeurs niet altijd in hun eentje.

Een programma komt pas tot leven als het reageert op de gebruiker. Een programma zonder gebruikersinteractie lijkt op een auto zonder stuur. Je kan een programma dingen laten doen met of zonder interactie met de gebruiker. Maar natuurlijk wordt het pas echt leuk wanneer de gebruiker merkt dat het programma op zijn acties reageert. Je zou als gebruiker immers al snel denken dat er iets fout loopt als het programma zo maar wat dingen doet (of niet doet).
De analogie met een auto zal het hopelijk allemaal wat duidelijker maken:

In een auto zit een motor en die motor bestaat op zijn beurt uit een hoop kleinere modules. Er zit een carburator, een pomp, cilinders, een radiator, waterpomp enz. in. Uiteraard is zo'n motor alleen niet voldoende. De gebruiker kan de motor en alle onderdelen hiervan interactief besturen. Wanneer hij aan de sleutel draait, start de motor. Als de bestuurder het ontkoppelingspedaal indrukt, de versnellingsbak in de juiste versnelling plaatst en vervolgens het pedaal zachtjes loslaat, komt de auto in beweging. De pedalen, sleutel, versnellingen... zijn interface-elementen die er voor zorgen dat de gebruiker interactief de motor kan aansturen. Het resultaat is dat de auto in beweging komt, de ruitenwissers beginnen bewegen, de lichten kunnen worden aangezet enz. Tussen die interface-elementen en de onderdelen van de motor liggen verbindingen, kabels, slangen enz. die ervoor zorgen dat alles netjes wordt aangestuurd.

Programma

auto

Bedieningsinterface

dashboard, pedalen, hendels...

Methodes

rijden, stoppen...

Functies

de motor die zorgt voor de aandrijving, de ruitenwissers (regenwater wegwissen), de waterpomp, de remmen...

Methodes horen dus bij het object “auto”. Functies staan daar los van. Meerdere objecten kunnen dezelfde functies delen. Het verschil zorgt vaak voor verwarring en je kan er inderdaad uren over discussiëren. Een methode hoort dus bij een “object”. Een functie staat los van het object.

2.2.4 Praktijkvoorbeeld 1: Processing

We testen het nu zelf eens uit in de programmeertaal Processing. Eerst moet je Processing (www.processing.org) downloaden en installeren. Kies de juiste versie. Tijdens de les demonstreren we hoe je de IDE installeert en start.

Processing maakt het makkelijker door enkel te spreken over functies en niet het verschil te maken tussen methodes en functies. In de setup()-functie bepaal je hoofdzakelijk het “uitzicht” van je programma zoals de breedte, de hoogte, de achtergrondkleur...

De draw()-functie is een soort container waarin wordt bepaald wat het programma moet tonen aan de gebruiker (bijvoorbeeld het webcambeeld, een tekening, een foto...).

 void setup() {
 	//algemene instellingen van het programma 
} void draw() { 
 	//wat moet er gebeuren?  
}

Het onderstaande Processing-voorbeeld tekent een witte cirkel van 300 pixels breed en hoog in het midden van een zwart "canvas" van 400 pixels breed en hoog.

 void setup(){
   	size(400, 400);
   	background(0);
   	stroke(255);
  	ellipse(200, 200, 300, 300);   
}   

Zowel size(), background(), stroke() als ellipse() zijn ingebouwde functies in Processing. Processing weet meteen wat hij moet doen als je zo'n functie oproept. Je kan ook parameters toevoegen. Denk nog eens even terug aan de auto. De knop zet de ruitenwissers in beweging, maar door aan het hendeltje te draaien, kan je de ruitenwissers sneller of trager of met een interval laten bewegen. Je kan dus aan een dezelfde “motor” meerdere parameters doorgeven. Zo gaat het ook in Processing. Als je geen parameters doorgeeft aan een bepaalde motor... excuseer, functie... dan valt Processing terug op een standaardinstelling. Uiteraard moet je goed weten hoeveel parameters elke functie kan ontvangen. Het heeft immers geen zin (en het zou enkel tot foutmeldingen leiden) als je zo maar wat parameters begint in te voeren. De parameters plaats je tussen de haakjes.

Bijvoorbeeld:

size(400, 400);

De twee getallen geven aan hoe breed en hoe hoog (in pixels uitgedrukt) het uiteindelijke programma moet weergegeven worden.

De regel

ellipse(200, 200, 300, 300);   

vertelt Processing dat hij een ellips van 300 pixels bij 300 pixels moet tekenen met zijn middelpunt op pixel x=200 en y=200. Bij een programma van 400 bij 400 pixels, staat de ellips precies in het midden. Je merkt dus ook dat de volgorde van de parameters wel degelijk van belang is in een Processing-sketch.

2.2.5 Praktijkvoorbeeld 2: javascript

Denk aan een auto, een Mercedes bijvoorbeeld. Daarbinnen heb je verschillende soorten die steevast een klasse worden genoemd, bijvoorbeeld de Mercedes C-klasse. Mensen herkennen andere mensen, dieren, koeien, weides, huizen, straten, auto's enz. In een programmeertaal zouden we dit geen “object” noemen, maar een klasse. Ook voor mensen zijn dit niet meteen zelfstandige objecten, we noemen dit “soortnamen”. Auto's zijn dus een klasse, maar jouw eigen auto beschouw je wel als een “object”. Het verschil tussen objecten en klassen zit in de meeste moderne programmeertalen. Wat je er mee doet?

Hieronder vind je een voorbeeld in javascript, de scriptingtaal (dus niet echt een programmeertaal: zie 2.2.2), die vooral bekend en populair is als de interactieve taal achter webpagina's. We bouwen een functie met de naam KindMaken, die natuurlijk niet echt doet wat ze belooft, maar we bouwen er wel een “denkbeeldig virtueel” kind mee. Niet dat er plots een kind op je computerscherm verschijnt. Elk kind heeft een aantal “parameters”, in het onderstaande geval een voornaam, familienaam, leeftijd, oogkleur, geslacht. Je kan het nog uitbreiden. Misschien raar om ook de leeftijd als parameter door te geven, want een pasgeboren kind is nog maar een paar ogenblikken oud. Denk echter naar analogie aan een game, die je moet bevolken met virtuele mensen (objecten). In zo'n geval is het wel belangrijk om de leeftijd te kunnen instellen.

Om het enigszins te verduidelijken staan de “zelfbedachte” woorden in het Nederlands, de ingebouwde woorden in het Engels. Met ingebouwde woorden bedoelen we in dit geval gereserveerde woorden die je niet zelf voor andere zaken kan gebruiken. Je kan dus niet de functieaanroep “alert” vervangen door bijvoorbeeld “gil”. Uiteraard verplicht niemand jou om Nederlands te gebruiken. De meeste programmeurs en ontwikkelaars schrijven alles in het Engels.

var KindMaken = function (Voornaam, Familienaam, Leeftijd, Oogkleur, Geslacht) {
    this.voornaam = Voornaam;
    this.familienaam = Familienaam;
    this.leeftijd = Leeftijd;
    this.oogkleur = Oogkleur;
    this.geslacht= Geslacht;
}
var papa= new KindMaken("Jan", "Peeters", "50", "blauw", "m");
var mama= new KindMaken("An", "Filips", "48", "groen", "v");
alert(papa.voornaam + " en " + mama.voornaam + " gaan trouwen.");

De afkorting var staat voor “variabele waarde. In dit geval zijn mama en papa twee variabelen en de functie (motor) kindmaken is dat ook, want je hebt die functie ook zelf gedefinieerd. Als je het bovenstaande stuk code kopieert en in een teksteditor (vb. Kladblok, zeker niet in een tekstverwerker) plakt en je bewaart het vervolgens als test.html. Dan kan je het eindresultaat bekijken in een webbrowser.

Probeer de code ook eens aan te passen. Je computer zal niet crashen als je een fout maakt. Al spelend leer je. Zo gaat het ook tijdens het programmeren of scripten.

2.2.6 Praktijkvoorbeeld 3:
in semi-natuurlijke taal met SIRK

Tja, misschien toch wel een beetje moeilijk voor een beginner. Programmeertalen zijn niet eenvoudig om te leren. Meer dan in menselijke taal zijn ze heel strikt gebonden aan regeltjes. Een fout stukje spelling of spraakkunst en … oeps, het werkt niet zoals je wil of zelfs helemaal niet. Je bent trouwens niet de enige die programmeren moeilijk vindt. Jef Raskin die in een grijs verleden de interface van de Apple Macintosh-computer ontwierp, schrijft:

“There is no question that modern systems are becoming increasingly complex and that programming tools need to accommodate this increasing complexity. Simple things have been made unnecessarily difficult, and we have failed to provide sufficient and sufficiently well-designed software tools needed to ease the difficulties of working in today's computer environment”.1


Maar als je toch echt even wil proberen, kan het ook met mijn hobbyproject SIRK dat je vindt op www.ardeco.be/sirk. De bedoeling van SIRK was om het programmeren een stapje dichter bij menselijke taal te brengen. Bovendien zijn de Engelse gereserveerde woorden hier vervangen door Nederlandstalige woorden met de bedoeling de indruk te wekken dat je kan programmeren in je eigen taal.

Stel dat je het getal 5 op je computerscherm wil weergeven (niet meteen hoogstaande kunst. Je zou denken: “Ik open MS Word en tik gewoon 5.”), dan is dit voor een programmeertaal een variabel gegeven. In plaats van de afkorting “var” gebruiken we binnen SIRK, gewoon zoals in mensentaal, de bepaalde lidwoorden “de” en “het”. Onze bedoeling is nu “het getal 5 afdrukken op het scherm.” We maken met SIRK dus eerst een getal aan met de waarde 5. We laten het onthouden door de computer (bewaar, store...). Zo kunnen we het later op elk gewenst moment op ons scherm afdrukken (GEBRUIKEN of @ en OP_HET_SCHERM of print).

het getal  
5 getal bewaar 
getal GEBRUIKEN OP_HET_SCHERM 

We zouden het ook op de volgende manier kunnen schrijven:

var number 
5 number store 
number @ print 

Op www.ardeco.be/sirk vind je nog een boel leukere voorbeelden. Probeer bijvoorbeeld de spelletjes eens uit of de tekenvoorbeelden.

2.2.7 Aan condities voldoen

In het echte leven hangt veel wat we doen af van de omstandigheden. Als het regent, open je een paraplu bijvoorbeeld. Doe je dit wanneer de zon schijnt, dan word je raar bekeken.

  1. In de meest eenvoudige vorm (0 of 1) kent een eenvoudig programma twee toestanden. Naar analogie gebruiken we de volgende situatie:

  2. Om zulke condities te testen, gebruiken heel wat programmeertalen een if-else-conditie. Soms werkt dit zelfs letterlijk voor “regen”. Bij een aantal auto's werken de ruitenwissers bijvoorbeeld als een sensor “regen” op de ruit “voelt”.

    In “pseudocode” zouden we dit zo kunnen uittekenen:

    var regen= testRegensensor(); 
    if(regen==1){
          Ruitenwissers(1); 
    }else{
          Ruitenwissers(0); 
    } 

Naast eenvoudige if/else-condities kan je ook gebruik maken van (beperkte) herhalingen (loop). Zoals bijvoorbeeld "beweeg 10 keer van links naar rechts op het scherm" of "teken 5 vierkanten met een willekeurige kleur op het scherm".

de xpositie 
de ypositie 
20 xpositie bewaar 
20 ypositie bewaar  
de breedte 
20 breedte bewaar 
de hoogte 
20 hoogte bewaar    
[
         xpositie gebruiken 40 + xpositie bewaar
         ypositie gebruiken 40 + ypositie bewaar
         kleur xpositie gebruiken ypositie gebruiken hoogte gebruiken breedte gebruiken vierkant  
] 5 keer  

Denk maar eens goed na waarvoor “40+” dient in de bovenstaande code.


1 RASKIN, J., The Humane Interface, New Directions for Designing Interactive Systems, Boston, 2010, 192-193.

home