Ecrire des itérateurs

Dans cette section, nous allons ajouter deux itérateurs dans la classe Carnet: Carnet#chaque_personne et Carnet#chaque_adresse. Au résultat, nous pourrons écrire ceci:

carnet.chaque_personne do |personne|
    # ...
end

carnet.chaque_adresse do |adresse|
    # ...
end

Le mot clef yield permet d'appeler un bloc de code. Voici un exemple:

def deux_fois
    yield
    yield
end

deux_fois { puts "Vive Ruby!" }

Ce qui produira:

Vive Ruby!
Vive Ruby!

Vous pouvez utiliser yield exactement comme n'importe quelle autre méthode. Pour passer des arguments à un bloc de code, passez-les simplement à yield. Prenez cet exemple:

def noms
    yield("Nicolas")
    yield("François")
    yield("Marina")
end

noms do |nom|
    puts "Salut " + nom + ", comment vas-tu?"
end

Ce qui affichera à l'écran:

Salut Nicolas, comment vas-tu?
Salut François, comment vas-tu?
Salut Marina, comment vas-tu?

Vous pouvez passez autant de paramètres que vous voulez au bloc de code. Par exemple:

def noms
    yield("Nicolas Rocher")
end

noms do |prenom, nom|
    puts prenom + " " + nom
end

Ce qui donnera:

Nicolas Rocher

Ce premier itérateur est le plus facile des deux à écrire: il suffit simplement de parcourir chaque personne dans le tableau @personnes et d'appeler yield sur chaque élément:

class Carnet
    # ...
    def chaque_personne
        @personnes.each { |p| yield(p) }
    end
end

Et voila!

Cet itérateur est quasi aussi simple à écrire que le premier. Au lieu de passer chaque personne au bloc de code, nous allons passer l'adresse de cette personne:

class Carnet
    # ...
    def chaque_adresse
        @personnes.each { |p| yield(p.adresse) }
    end
end

Juste pour tout mettre au clair, voici le code complet commenté de la classe Carnet. Il s'agit d'un morceau de code assez complexe, mais en découpant les tâches au fur et à mesure, il est beaucoup plus facile à maintenir:

class Carnet 
    #
    #  Méthodes fondamentales:
    #    initialize 
    #    ajoute 
    #    retire 
    #
    def initialize
        @personnes = []
    end
    def ajoute(personne)
        @personnes.push(personne)
        @personnes = @personnes.sort { |a, b| par_nom(a, b) }  
    end
    def remove(personne)   
        @personnes.delete(personne)
    end
     
    #
    #  Iterateurs: 
    #    chaque_personne
    #    chaque_adresse 
    #
    def chaque_personne 
        @personnes.each { |p| yield p }
    end
    def chaque_adresse
        @personnes.each { |p| yield p.adresse }
    end
        
    #  
    #  Fonction de tri. 
    #
    def par_nom(a, b)
        if a.prenom == b.prenom
            a.nom <=> b.nom
        else
            a.prenom <=> b.prenom
        end 
    end
end