#Klooienmetcomputers

Iterators

Arnout van Kempen over rommelen in een digitale wereld.

Stel, we hebben een lijst getallen en die willen we op het scherm zetten. In C krijg je dan zoiets als

#include<stdio.h> 

int main() {
    int array[5] = {1, 2, 3, 4, 5};
    int totaal = 0;
    for(int i = 0; i<5; i++) {
        totaal = totaal + array[i];
    }
    printf(“totaal is %d\n”, totaal);
}

Je moet zelf de logica van de "iteratie" opzetten. Een variabele i, met een startwaarde, een type, een controle wanneer de iteratie moet stoppen, iets om naar het volgende element in de array te gaan. Dat staat allemaal tussen de haken van for.

De meest letterlijke vertaling van dit voorbeeld naar Rust zou zijn: 

fn main() {
    let array = [1, 2, 3, 4, 5];
    let mut totaal = 0; 

    for i in 0..5 {
        totaal += array[i];
    }

    println!("totaal is {}", totaal);
}

Type-aanduidingen ontbreken, omdat Rust die allemaal zelf wel kan bedenken. Maar belangrijker: alle logica van de for opdracht beperkt zich tot het aanduiden van de range van 0 tot 5. (0..=4 had ook gekund, dat is 0 tot en met 4).

Maar je moet nu nog steeds bedenken dat de array 5 elementen heeft, genummerd van 0 tot en met 4. Dat kan fout gaan en Rust houdt niet van fouten. Om nog wat programma-logica weg te halen bij de programmeur, gebruikt Rust iterators. Iedere variabele die in feite een groep van waarden heeft, zoals een array of een vector, kan je daarmee benaderen. De code wordt nu:

fn main() {
    let array = [1, 2, 3, 4, 5];
    let mut totaal = 0; 

    for element in array.iter() {
        totaal += element;
    } 

    println!("totaal is {}", totaal);
}

Alle foutgevoelige logica laten we nu aan Rust over. .iter() levert alle elementen van array op, iedere keer dat je deze methode aanroept. Ideaal voor onze for opdracht.

Het kan echter nog simpeler en nog "Rust-iger" met wat in Rust een iterator consumer heet. Dit is een methode die de iterator aanroept, iets doet met de verkregen waarde en dan herhaalt, tot het einde van de reeks is bereikt. In ons geval is dat .sum(). Daarmee wordt ons programma slechts dit:

fn main() {
    let array = [1, 2, 3, 4, 5];
    let totaal: i32 = array.iter().sum(); 

    println!("totaal is {}", totaal);
}

Omdat de sum-methode verschillende types kan opleveren, moeten we bij de declaratie van totaal een type aangeven, maar verder laten we al het denkwerk aan Rust.

Het aardige van iterators is dat ze lazy zijn. Pas als ze worden aangeroepen wordt daadwerkelijk de iteratie uitgevoerd. Voor ons voorbeeld betekent dat nog niet zoveel, maar als de code complexer wordt, gaat dat wel tellen.

De vorige keer had ik aangegeven dat er een verband is tussen closures en iterators. Laten we dat illustreren met het voorbeeld dat we hebben, met een kleine uitbreiding: we willen de som van de kwadraten van de array hebben.

Met de methode .map(closure) maak je van een iterator een nieuwe iterator, waarop je de closure hebt toegepast, via een verwijzing naar de elementen uit de oorspronkelijke iterator. Dat betekent dat het antwoord dat we zoeken er zo uit gaat zien:

fn main() {
    let array = [1, 2, 3, 4, 5];
    let totaal: i32 = array.iter().map(|&x| x * x).sum(); 

    println!("De som van de kwadraten is {}", totaal);
}

het argument van de closure is de verwijzing naar de iterator over de array, met &x. De onderliggende waarde x vermenigvuldigen we met zichzelf en de sum-methode telt dat allemaal op.

En ja, natuurlijk had dat ook op de C manier gekund. Ter vergelijking, wat daar het dichtste bij komt was dit geweest: 

fn main() {
    let array = [1, 2, 3, 4, 5];
    let mut totaal = 0;
    let mut i = 0; 

    while i < 5 {
        totaal += array[i] * array[i];
        i += 1;
    }

    println!("De som van de kwadraten is {}", totaal);
}

Het kan, maar het is foutgevoelig en niet erg "Rust".

Wie mee wil doen met #klooienmetcomputers kan dat doen via GitHub. Maak een account op github.com en zoek naar Abmvk/kmc. Het account Abmvk volgen kan ook. Lezers zijn vrij te gebruiken wat ze willen en om zelf zaken toe te voegen of aan te passen, vragen te stellen of commentaar te leveren.

Arnout van Kempen di CCO CISA is Senior manager Risk & Compliance bij Baker Tilly. Hij schrijft op persoonlijke titel. Hij is lid van de Commissie Financiƫle verslaggeving & Accountancy van de AFM en lid van de signaleringsraad van de NBA. Daarnaast is hij diaken van het bisdom 's-Hertogenbosch.

Gerelateerd

reacties

Reageer op dit artikel

Spelregels debat

    Aanmelden nieuwsbrief

    Ontvang elke werkdag (maandag t/m vrijdag) de laatste nieuwsberichten, opinies en artikelen in uw mailbox.

    Bent u NBA-lid? Dan kunt u zich ook aanmelden via uw ledenprofiel op MijnNBA.nl.