Partition
Une utilisation rigolote de try_for_each…
On prend en entrée une tranche de i32.
On cherche à savoir s’il est possible de la décomposer
en trois sous-tranches S1, S2, S3 telles que:
- les sommes des elements de chaque tranche sont égales
- toutes les tranches sont disjointes
- l’union des tranches forme S
Attention il s’agit de tranches, le problème n’est donc pas NP-dur.
Voici un petit jeu de tests:
fn main() {
    let v = vec![1, 5, -1, 2, 3, 2, 3, -2, 2];
    assert!(partition3(&v));
    let w = vec![1, 3, 2, 4, 2, 3, 5, 3];
    assert!(!partition3(&w));
    let x = vec![5, 6, 5, -1];
    assert!(!partition3(&x));
}
Le but du jeu est d’écrire un code fonctionnel de coût linéaire.
Solution
Commençons par l’idée principale. On commence par calculer la somme de toute la tranche, de manière à calculer la quantité à mettre dans chaque sous tranche:
fn partition3(tranche: &[i32]) -> bool {
    let somme = tranche.iter().sum();
On va ensuite itérer sur toutes les sommes partielles de s à l’aide de scan.
Les sommes partielles commencent à 0 et terminent à somme.
Si il est possible de partitionner alors,
- à la fin de S1, la somme partielle est somme/3
- à la fin de S2, la somme partielle est 2*somme/3
Cette propriété est valable même si plusieurs solutions sont possibles.
    somme % 3 == 0
        && sous_sequence(
            vec![somme / 3, 2 * somme / 3, somme],
            tranche.iter().scan(0, |s, e| {
                *s += e;
                Some(*s)
            }),
        )
}
Il nous reste juste à vérifier que la séquence des tiers de somme est une sous-séquence de toutes les sommes partielles.
Est-ce que vous voyez comment faire (j’utilise try_for_each)?
Sous Séquence
Voici ma solution:
fn sous_sequence<T, I, J>(i: I, j: J) -> bool
where
    T: Eq,
    I: IntoIterator<Item = T>,
    J: IntoIterator<Item = T>,
{
    let mut iter_j = j.into_iter();
    i.into_iter().all(|ei| {
        iter_j
            .try_for_each(|ej| if ei == ej { Err(()) } else { Ok(()) })
            .is_err()
    })
}
Le all me permet de vérifier que tous les éléments i sont bien dans j.
Notons que l’on peut s’arrêter avant la fin de j mais que c’est une ‘feature’, la
somme des éléments restants valant alors 0.
Le truc délicat c’est qu’il faut à chaque fois repartir du dernier endroit où l’on s’était
arrêté lors du parcours de j.
Toutes les opérations en try_.... sur les itérateurs permettent exactement ça.
Si on regarde le prototype:
fn try_for_each<F, R>(&mut self, f: F) -> R where
    F: FnMut(Self::Item) -> R,
        R: Try<Ok = ()>,
on se rend compte que l’itérateur n’est pas consommé mais emprunté en écriture.
Le try_fold en particulier est important en rust et il est conseillé de l’implémenter
manuellement lorsque l’on développe un itérateur s’il est possible d’avoir une implémentation
meilleure que celle par défaut.