Iterator::scan în Rust

Ca fold, dar produce câte un rezultat după fiecare element: o stare actualizată pas cu pas, de exemplu un total cumulat.

Sintaxă
fn scan<St, B, F>(self, initial_state: St, f: F) -> Scan<Self, St, F>
where F: FnMut(&mut St, Self::Item) -> Option<B>

Ce face Iterator::scan

scan(stare_initiala, |stare, x| ...) ține o stare mutabilă pe care closure-ul o primește ca &mut la fiecare element. Closure-ul returnează Option: Some(y) produce y, iar None oprește iteratorul.

Cazul tipic este suma cumulată (soldul după fiecare tranzacție, totalul pe zile). Spre deosebire de fold, care dă doar rezultatul final, scan dă toate rezultatele intermediare. Ca toate adaptoarele, este leneș: nu face nimic până când iteratorul nu este consumat (de for, collect, sum, count etc.).

Parametri

Parametru Descriere
initial_state Valoarea de pornire a stării.
f Closure care primește &mut stare și elementul curent, și returnează Some(valoare) sau None pentru oprire.

Ce returnează

Un iterator Scan.

Exemple

Exemplul 1: Total cumulat al vânzărilor

fn main() {
    let vanzari = [120, 80, 200, 50, 150];
    let cumulat: Vec<u32> = vanzari
        .iter()
        .scan(0, |total, &v| {
            *total += v;
            Some(*total)
        })
        .collect();
    println!("pe zile:  {:?}", vanzari);
    println!("cumulat:  {:?}", cumulat);
}

Rezultat

pe zile:  [120, 80, 200, 50, 150]
cumulat:  [120, 200, 400, 450, 600]

Exemplul 2: Soldul contului, oprit la primul minus

Când soldul ar deveni negativ, returnăm None, iar tranzacțiile de după nu mai sunt procesate.

fn main() {
    let tranzactii = [100, -30, -50, -40, 200];
    let solduri: Vec<i32> = tranzactii
        .iter()
        .scan(0, |sold, &t| {
            if *sold + t < 0 {
                return None;
            }
            *sold += t;
            Some(*sold)
        })
        .collect();
    println!("solduri: {:?}", solduri);
    println!("tranzacții acceptate: {} din {}", solduri.len(), tranzactii.len());
}

Rezultat

solduri: [100, 70, 20]
tranzacții acceptate: 3 din 5

Greșeli frecvente

  • Folosești scan când ai nevoie doar de rezultatul final: fold sau sum sunt mai simple.
  • Uiți steluța la stare: total += v nu compilează, pentru că total este &mut. Scrie *total += v.

Comenzi înrudite

  • Iterator::fold: Combină toate elementele într-o singură valoare, pornind de la o valoare inițială și aplicând o funcție pas cu pas.
  • Iterator::map_while: Transformă elementele cât timp funcția returnează Some și se oprește la primul None.
  • Iterator::try_fold: Ca fold, dar funcția returnează Option sau Result și oprirea are loc la primul None sau Err.
  • iter::successors: Creează un șir în care fiecare element se calculează din cel dinainte; se oprește când funcția returnează None.