Control flow

Niet te verwarren met Flow control.

Control flow of besturingsstroom is een begrip uit de informatica. Het slaat op de (niet-lineaire) volgorde van uitvoering van de instructies in een computerprogramma. Control flow wordt meestal bepaald door het gebruik van besturingsstructuren zoals lussen (while, for), beslissingsstructuren (if, case), spronginstructies en het aanroepen van subprogrammas.

Labels en goto

De meest elementaire vorm van een besturingsstroom is het gebruik van goto in combinatie met een if-opdracht. Hierbij wordt meestal een label gebruikt om aan te geven waarheen gesprongen moet worden.

Hoewel op deze manier alle verderop genoemde vormen van control flow kunnen worden verwezenlijkt, wordt deze in de meeste programmeertalen niet gebruikt, of wordt het gebruik ontraden, vanwege de slechte programmastructuur die dit oplevert.

Besturingsstructuren

Alle programmeertalen behalve smaltalk kennen besturingsstructuren.

Hierbij wordt een blok van statements (uitvoeringsblok ) gecontroleerd door een simpele structuur die voor of om dit uitvoeringsblok is geplaatst. Deze controle houdt in dat gestuurd wordt of, en zo ja hoe vaak het uitvoeringsblok wordt uitgevoerd.

Een besturingsstructuur wordt altijd begonnen met een sleutelwoord, meestal gevolgd door een besturingsuitdrukking (expressie).

Voorbeelden van deze sleutelwoorden zijn: for, while, if, switch en case

Hoe het einde van het uitvoeringsblok wordt aangegeven is verschillend voor verschillende talen.

  • geen specifiek sleutelwoord aan het eind van het blok, maar een afbakening van het blok:
    • in C, C++, C#, java en javascript wordt het blok omsloten door accolades {...}, of, in geval van een enkele statement mogen zelfs deze accolades ontbreken.
    • In pascal en oudere algol-dialecten wordt begin...end gebruikt.
    • In PLM wordt DO..END gebruikt.
  • specifiek sleutelwoord aan het eind:
    • In latere algol-dialecten wordt het sleutelwoord achterwaarts gebruikt: if...fi of case...esac
    • Fortran, Visual Basic, Ada gebruiken end + het sleutelwoord: if...end if

In principe zijn alle besturingsstructuren onder te verdelen in:

  • keuzestructuren, hierbij wordt het uitvoeringsblok niet of wel doorlopen (dus 0 of 1 keer), afhankelijk van de besturingsuidrukking.
  • lussen, hierbij kan het uitvoeringsblok meermalen worden doorlopen.

Keuzestructuren

Test

Deze structuur vindt men in alle programmeertalen. Afhankelijk van de conditie wordt het uitvoeringsblok uitgevoerd. Vaak ziet deze er als volgt uit:

if (conditie)
{
    ...
}

In andere talen, onder andere Basic, ziet het er zo uit:

if conditie then
    ...
end if

Verder is er vaak een alternatief uitvoeringsblok voor wanneer niet aan de conditie wordt voldaan:

if (conditie)
{
    ...
}
else
{
    ...
}

In Basic kennen we deze notatie ook, maar in heel oude versies wordt echter het alternatieve uitvoeringsblok niet ondersteund. BASIC versie 7.0 van de Commodore 128 kent die notatie alleen met een BEGIN...BEND zoals hieronder is te zien:

IF conditie THEN BEGIN
    ...
BEND:ELSE BEGIN
    ...
BEND

Ook zien we vaak dat ook nog op een tweede (en volgende ) conditie getest kan worden, met bijbehorende uitvoeringsblokken:

if (conditie1)
{
    ...
}
elseif (conditie2)
{
    ...
}
else
{
    ...
}

Ook Basic kent deze structuur zoals Visual Basic, maar oudere versies en BASIC 7.0 ondersteunen deze niet.

Keuze

In veel talen is het ook mogelijk om op basis van 1 getal een van de vele uitvoeringsblokken te selecteren:

switch (waarde)
{
case 1: // uitvoeringsblok 1
   ...
case 2: // uitvoeringsblok 2
   ...
case 3: // uitvoeringsblok 3
   ...
case 4: // uitvoeringsblok 4
   ...
case 5: // uitvoeringsblok 5
   ...
default:
}

In verschillende Basic dialecten werkt het zo:

select case waarde
    case 1 ' uitvoeringsblok 1
        ...
    case 2 ' uitvoeringsblok 2
        ...
    case 3 ' uitvoeringsblok 3
        ...
    case 4 ' uitvoeringsblok 4
        ...
    case 5 ' uitvoeringsblok 5
        ...
    case else
        ...
end select

Lussen

In een lus wordt een uitvoeringsblok meermalen doorlopen. Iedere keer dat deze doorlopen wordt, wordt een iteratie genoemd. De belangrijkste lussen worden hieronder genoemd.

Conditiegestuurde lussen

Bij deze vorm van een lus wordt, zolang er aan een voorwaarde wordt voldaan, het uitvoeringsblok uitgevoerd.

We komen dit tegen in de meeste moderne programmeertalen (in ieder geval in C, C++ C#, java, javascript, pascal ook in latere dialecten van algol) in de vorm:

while (conditie)
{
    ...
}

en

do 
{
    ...
} while (conditie);

Ook Basic kent deze voorwaardelijke lussen hetgeen weer de accolades verdwijnen. Een while loop moet echter eindigen met een wend en in Visual Basic .NET zelfs met end while. Bij de laatste voorwaardelijke lus wordt het uitvoeringsblok altijd in ieder geval 1 keer doorlopen.

Bij oudere talen kom je ook tegen: repeat ... until conditie

Tellergestuurde lussen

Deze lussen zijn bedoeld om het uitvoeringsblok een vooraf ingesteld aantal keren te doorlopen. Alle programmeertalen van de laatste 25 jaar kennen een dergelijke constructie, en zelfs een aantal assemblers (bv de 80386 assembler en hoger) hebben een dergelijke lus standaard ingebouwd.

Binnen de lus kan de teller nog gebruikt worden in de statements.

In C, C++, C#, Java en Javascript kan een tellergestuurde lus er zo uitzien:

for (i = 0; i < einde; i++)
{
    ...
}

In Basic zo:

For i = 1 To einde Step 1
    ...
Next i

Verzamelinggestuurde lussen

Deze lussen zijn bedoeld om het uitvoeringsblok voor ieder element (van een bepaald type) van een verzameling te doorlopen. We komen deze constructie tegen in C#, Visual Basic, Visual Basic .NET, javascript, Smaltalk en Perl.

In C# kan zoiets er zo uitzien:

foreach (Kat k in MijnDierenVerzameling)
{
   ...
}

In Visual Basic en Visual Basic .NET zal hetzelfde er als volgt uitzien.

Dim K As Kat
For Each K In MijnDierenVerzameling
   ...
Next K

Let er op dat MijnDierenVerzameling ook honden kan bevatten, maar deze worden niet aan het uitvoeringsblok aangeboden.

Bijzondere mogelijkheden binnen een lus

Over het algemeen voorziet een programmeertaal in mogelijkheden om een loop voortijdig af te breken. Hiermee wordt dan de voorwaarde die in de besturingsuitdrukking zit gepasseerd. In veel talen kan dit met het break statement. Javascript heeft hierbij als enige taal de mogelijkheid om aan break een label mee te geven, zodat in één keer meer geneste lussen verlaten kunnen worden In basic wordt hiervoor Exit gevolgd door de loop soort gebruikt (bv Exit For).

Ook is het vaak mogelijk om voor deze keer de rest van het uitvoeringsblok over te slaan, en gelijk door te gaan met de volgende iteratie. Hiervoor wordt vaak het continue-statement gebruikt.

Recursie als alternatief voor een lus

Als een functie zichzelf herhaaldelijk aanroept, wordt dit recursie genoemd. Op deze manier zijn veel zaken die met een lus mogelijk zijn ook te verwerkelijken. Dat kan ook in Basic worden gebruikt.

Voorbeeld: De som van alle getallen tot getal

  1 + 2 + .... + getal

Voorbeeld van een recursieve functie, en een loop die hetzelfde doet

int TelOpTot(int getal)
{
   if (getal > 0)
   {
      return (getal + TelOpTot(getal - 1));
   }
   return 0;
}
int TelOpTot(int getal)
{
   int resultaat = 0
   while (getal > 0)
   {
      resultaat += getal;
      getal--;
   }
   return 0;
}

Oneindige lussen

Zie Oneindige lus voor het hoofdartikel over dit onderwerp.

Sommige lussen worden (onbedoeld) oneindig uitgevoerd. Dit soort lussen kunnen ontstaan door het ontbreken van een voorwaarde om de lus te beëindigen of een voorwaarde waar nooit aan voldaan wordt (of kan worden). Een recursie die niet termineert kan ook een reden zijn waarom een oneindige lus ontstaat.

Dode code

Zie Dode code voor het hoofdartikel over dit onderwerp.

Het is mogelijk dat bepaalde stukken code nooit uitgevoerd kunnen worden. Deze stukken code worden dode code genoemd. Een compiler kan de besturingsstroom van een programma analyseren om de dode code op te sporen.

Trivia

Bij een van de eerste assembleertalen werd de besturingsstroom uitsluitend gedaan door middel van een speciale testinstructie. Afhankelijk van het resultaat van deze test instructie werd de volgende instructie overgeslagen of uitgevoerd.

Gebruikelijk was om dan de volgende opdracht een sprongopdracht te laten zijn.