saverioriotto.it

Rust e Programmazione Funzionale: Closure, Map e Filter

Scopri come Rust integra concetti di programmazione funzionale: closure, funzioni di ordine superiore e trasformazione dei dati con map, filter e fold. Esempi pratici inclusi!

Rust e Programmazione Funzionale: Closure, Map e Filter

Rust è noto per il suo approccio alla programmazione sicura e ad alte prestazioni, ma supporta anche molti principi della programmazione funzionale. Le closure consentono di creare funzioni anonime flessibili, mentre metodi come map, filter e fold permettono di trasformare dati in modo dichiarativo. Questo articolo esplora come utilizzare queste funzionalità in Rust per scrivere codice conciso ed espressivo.

Closure: Definizione e Usi

Le closure sono funzioni anonime che possono catturare variabili dall'ambiente circostante. In Rust, sono definite con il simbolo | e possono essere assegnate a variabili, passate come argomenti o utilizzate inline.

Esempio Base di Closure

fn main() {
    let quadrato = |x: i32| x * x;
    println!("Il quadrato di 4 è {}", quadrato(4));
}

Qui, quadrato è una closure che accetta un numero intero e restituisce il suo quadrato.

Closure con Cattura dell’Ambiente

Le closure possono catturare variabili dal loro contesto.

fn main() {
    let moltiplicatore = 2;
    let moltiplica = |x: i32| x * moltiplicatore;
    println!("2 x 5 = {}", moltiplica(5));
}

In questo esempio, la closure moltiplica cattura il valore di moltiplicatore dall'ambiente.

Tipizzazione Inferenziale

Rust può dedurre i tipi delle closure, ma puoi anche specificarli manualmente.

let add = |x: i32, y: i32| -> i32 { x + y };

Funzioni di Ordine Superiore

Le funzioni di ordine superiore sono funzioni che accettano altre funzioni come argomenti o restituiscono funzioni. Rust supporta questo paradigma in modo naturale.

Esempio: Passare una Closure a una Funzione

fn applica_funzione(f: F, x: i32) -> i32
where
    F: Fn(i32) -> i32,
{
    f(x)
}

fn main() {
    let quadrato = |x: i32| x * x;
    println!("Risultato: {}", applica_funzione(quadrato, 3));
}

La funzione applica_funzione accetta una closure f e un numero x, applicando la closure al numero.

Trasformare Dati con map, filter e fold

Rust fornisce metodi funzionali per iterare e trasformare collezioni in modo dichiarativo.

map: Trasformare Elementi

Il metodo map applica una funzione a ogni elemento di una collezione, restituendo una nuova collezione con i risultati.

fn main() {
    let numeri = vec![1, 2, 3, 4, 5];
    let doppi = numeri.iter().map(|x| x * 2).collect::>();
    println!("Numeri doppi: {:?}", doppi);
}

filter: Filtrare Elementi

filter seleziona solo gli elementi che soddisfano una determinata condizione.

fn main() {
    let numeri = vec![1, 2, 3, 4, 5];
    let pari = numeri.iter().filter(|&&x| x % 2 == 0).collect::>();
    println!("Numeri pari: {:?}", pari);
}

fold: Ridurre una Collezione

fold riduce una collezione a un unico valore, combinando gli elementi secondo una funzione.

fn main() {
    let numeri = vec![1, 2, 3, 4, 5];
    let somma = numeri.iter().fold(0, |acc, x| acc + x);
    println!("Somma: {}", somma);
}

Esercizi Pratici

  1. Moltiplicare Valori di un Vettore
    Scrivi una funzione che accetta un vettore di numeri e restituisce un nuovo vettore con i valori moltiplicati per 3 utilizzando map.

    Soluzione:

    fn moltiplica(vettore: Vec) -> Vec {
        vettore.into_iter().map(|x| x * 3).collect()
    }
    
    fn main() {
        let numeri = vec![1, 2, 3];
        println!("{:?}", moltiplica(numeri));
    }
  2. Filtrare Numeri Negativi
    Scrivi una funzione che rimuove i numeri negativi da un vettore utilizzando filter.

    Soluzione:

    fn filtra_positivi(vettore: Vec) -> Vec {
        vettore.into_iter().filter(|&x| x >= 0).collect()
    }
    
    fn main() {
        let numeri = vec![-1, 0, 1, 2, -3];
        println!("{:?}", filtra_positivi(numeri));
    }
  3. Prodotto di Tutti i Numeri
    Utilizza fold per calcolare il prodotto di tutti i numeri in un vettore.

    Soluzione:

    fn prodotto(vettore: Vec) -> i32 {
        vettore.into_iter().fold(1, |acc, x| acc * x)
    }
    
    fn main() {
        let numeri = vec![1, 2, 3, 4];
        println!("Prodotto: {}", prodotto(numeri));
    }

Conclusione

Rust combina sicurezza e performance con caratteristiche della programmazione funzionale, permettendo di scrivere codice conciso e leggibile. Le closure, le funzioni di ordine superiore e metodi funzionali come map, filter e fold ti offrono strumenti potenti per manipolare i dati. Nel prossimo articolo vedremo le Concurrency in Rust, quindi come creare e gestire thread, comunicare in sicurezza con i canali e utilizzare async/await per la programmazione asincrona.




Commenti
* Obbligatorio