Skip to content

Latest commit

 

History

History
151 lines (102 loc) · 3.33 KB

lambda.md

File metadata and controls

151 lines (102 loc) · 3.33 KB

Implementiamo una serie di operazioni su array, e vogliamo farlo scrivendo meno codice possibile.

Supponiamo di avere un array A di 4 elementi.

A = [1, 2, 3, 4, ]

Vogliamo calcolare 2*A, ovvero un array B contenente il doppio dei di ogni elemento.

B = [2, 4, 6, 8, ]

Va bene, possiamo scrivere una semplice funzione che, per ogni elmento a di A, inserisce nell'array B il doppio di a, ovvero 2*a

vector<int> doppio(vector<int> A) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(a * 2);
    }

    return B;
}

Vogliamo ora calcolare anche 3*A. Okay, questo è relativamente semplice e lo possiamo fare modificando la funzione precedente che abbiamo appena scritto aggiungendo un parametro.

vector<int> moltiplica(vector<int> A, int m) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(a * m);
    }

    return B;
}

E se ora volessimo calcolare invece 3+A, ovvero un array C come il seguente contenente non più il tripo degli elementi di A ma gli elementi più 3?

C = [5, 7, 9, 11, ]

Mmmh... dovremmo scrivere un'altra funzione? Okay, facciamolo.

vector<int> somma(vector<int> A, int x) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(a * m);
    }

    return B;
}

A questo punto abbiamo moltiplica e somma, che adempiono a funzioni diverse ma abbastanza parametrizzate da coprire tutti i casi di loro competenza.

E se a questo punto volessimo calcolare la funzione potenza di 2 dell'array A?

Dovremmo scrivere un'altra funzione... Che noia!

Facciamolo, ma parametrizziamola subito di modo da non dover poi scrivere altre funzioni!

#include <cmath>

vector<int> pow2(vector<int> A, int x) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(std::pow(a, x));
    }

    return B;
}

Riguardiamo ora tutte le funzioni implementate. Cosa cambia tra queste?

Quasi nulla! Più del 90% del codice scritto è lo stesso per tutte e tre le funzioni, cambia solo l'operazione da fare su ogni elemento.

Ma... e se prendessimo in input proprio la funzione da applicare ai valori?

vector<int> trasforma(vector<int> A, ??? func) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(func(a));
    }

    return B;
}

Ci manca qualcosa. Innanzitutto qual è il tipo di una funzione? Una funzione è identificata dal tipo di ritorno e dal tipo e numero dei suoi parametri. Wait, ma le funzioni sono codice. Come facciamo a passare del codice in input ad una funzione?

Mmmh, passiamo il nome no? Ah! Un puntatore!

Proviamo

vector<int> trasforma(vector<int> A, int (*func)(int)) {
    vector<int> B;

    for (auto a: A) {
        B.push_back(func(a));
    }

    return B;
}

Okay, ma come chiamiamo trasforma?

vector<int> A = {1, 2, 3, 4};
vector<int> B;

B = trasforma(A, ???)

Dobbiamo passare una funzione che prende un int e restituisce un int.

int doub(int x) {
    return x * 2;
}

B = trasforma(A, doub)

Cosa abbiamo guadagnato?

Abbiamo generalizzato l'algoritmo per scorrere l'array, indipendentemente dal fatto che questo sia veramente un array o meno. Trasformiamo collezioni! L'utente ora può preoccuparsi solo di costruire la funzione che lavora sul singolo elemento!

#include <cmath>

int pow2(int x) {
    return std::pow(x, 2);
}

B = trasforma(A, doub)