1 00:00:00,660 --> 00:00:03,030 Dans cette séquence, on va voir les itérateurs. 2 00:00:03,230 --> 00:00:05,330 On va revenir sur les collections et voir comment est-ce 3 00:00:05,530 --> 00:00:08,720 qu'on peut les parcourir facilement, grâce aux itérateurs en Pharo. 4 00:00:09,300 --> 00:00:12,250 Vous allez comprendre la puissance des itérateurs en 5 00:00:12,450 --> 00:00:14,840 Pharo, et puis je vais vous donner un panorama des 6 00:00:15,040 --> 00:00:17,470 principaux itérateurs que vous pouvez utiliser sur vos collections 7 00:00:18,170 --> 00:00:21,270 Juste un exemple. Ça, c'est le code que vous devriez 8 00:00:21,470 --> 00:00:25,330 écrire par exemple en Java pour parcourir une collection. 9 00:00:25,630 --> 00:00:29,200 Je veux parcourir une collection "person", pour extraire 10 00:00:29,400 --> 00:00:31,900 la liste des noms de ces personnes, de ces objets "person". 11 00:00:32,730 --> 00:00:35,640 En fait en Pharo, on écrirait plutôt ce code-là, on 12 00:00:35,840 --> 00:00:38,890 utiliserait un itérateur, qu'on reverra dans la suite du 13 00:00:39,090 --> 00:00:43,000 cours, "collect", où on va collecter l'ensemble des noms des personnes. 14 00:00:44,200 --> 00:00:47,760 En Java 8 pour l'anecdote, la dernière version de Java, 15 00:00:48,210 --> 00:00:52,100 ils ont introduit la notion de blocks, l'équivalent des 16 00:00:52,300 --> 00:00:54,130 blocks SmallTalk, les fermetures lexicales, qui leur 17 00:00:54,330 --> 00:00:57,000 permet d'avoir une syntaxe qui est proche de celle de Pharo. 18 00:00:57,220 --> 00:00:59,560 Sauf qu'en Pharo, on l'a depuis très longtemps, depuis le 19 00:00:59,760 --> 00:01:04,180 début et elle est au cœur du langage, ce qui donne une 20 00:01:04,420 --> 00:01:06,470 puissance d'expression aux programmeurs assez importante. 21 00:01:08,580 --> 00:01:11,460 Les itérateurs, il en existe plein. 22 00:01:11,660 --> 00:01:13,580 Le premier qu'on voit, c'est "collect". 23 00:01:13,910 --> 00:01:16,620 A quoi sert collect quand je l'envoie à une collection? 24 00:01:16,820 --> 00:01:18,900 Ici on a une collection de nombres, donc des nombres 25 00:01:19,100 --> 00:01:21,590 positifs et des nombres négatifs. J'envoie le message 26 00:01:21,790 --> 00:01:23,430 "collect" à cette collection et je lui passe un block. 27 00:01:23,810 --> 00:01:26,660 A chaque tour de la collection, le paramètre du block 28 00:01:26,860 --> 00:01:29,920 vaudra successivement 2, moins 3, 4, et caetera. 29 00:01:30,260 --> 00:01:33,560 Et puis on va envoyer le abs, donc abs c'est absolu. 30 00:01:33,760 --> 00:01:36,590 Donc ça veut dire qu'on va demander la valeur absolue de ce nombre. 31 00:01:37,830 --> 00:01:39,620 Une fois qu'on aura appliqué ce block à chacun des 32 00:01:39,820 --> 00:01:41,920 éléments de la collection, on va agréger tous les 33 00:01:42,120 --> 00:01:43,170 résultats dans une nouvelle collection. 34 00:01:43,760 --> 00:01:45,410 On voit que le résultat rendu par collect c'est une 35 00:01:45,610 --> 00:01:48,900 nouvelle collection, telle qu'on a appliqué ce block à 36 00:01:49,100 --> 00:01:50,180 chacun des éléments de la collection. 37 00:01:50,540 --> 00:01:52,740 On a pris la valeur absolue de 2; la valeur absolue de 38 00:01:52,940 --> 00:01:56,120 moins 3, donc 3; la valeur absolue de 4, 4; la valeur 39 00:01:56,320 --> 00:01:59,910 absolue de moins 35, ça fait 35 et la valeur absolue de 4 ça fait 4. 40 00:02:01,720 --> 00:02:06,440 Le point à retenir qui est vraiment intéressant, c'est qu'on pense objet. 41 00:02:07,050 --> 00:02:09,400 On demande à la collection de faire quelque chose pour 42 00:02:09,600 --> 00:02:12,760 nous, donc c'est la collection qui va se parcourir les 43 00:02:13,000 --> 00:02:15,630 éléments qu'elle contient elle-même et on va lui fournir 44 00:02:15,830 --> 00:02:17,200 le traitement à appliquer sur chacun des éléments. 45 00:02:17,530 --> 00:02:19,400 C'est là qu'est le secret des itérateurs. 46 00:02:21,200 --> 00:02:22,930 Voici un nouvel exemple de collect. 47 00:02:23,670 --> 00:02:26,110 J'ai une collection ici, je lui envoie le message 48 00:02:26,440 --> 00:02:30,900 "collect" et dans le block je vais à chaque fois demander 49 00:02:31,720 --> 00:02:33,900 "Est-ce que l'élément est impair ?". 50 00:02:34,460 --> 00:02:36,820 Et donc ici pour le premier élément, je vais agréger tous 51 00:02:37,020 --> 00:02:39,860 les résultats: false, true, false et true. 52 00:02:44,840 --> 00:02:49,390 Vous pouvez naturellement écrire ce qu'on aurait l'habitude 53 00:02:49,590 --> 00:02:51,630 d'écrire dans notre langage où les blocks et les 54 00:02:51,830 --> 00:02:54,240 itérateurs n'existent pas, donc on pourrait écrire "Je 55 00:02:54,440 --> 00:02:57,950 prends la collection, je vais construire une collection 56 00:02:58,150 --> 00:03:01,830 de résultats et je vais parcourir de 1 à la taille de la 57 00:03:02,030 --> 00:03:03,020 collection, collection size. 58 00:03:03,660 --> 00:03:04,820 Je vais utiliser un do. 59 00:03:05,110 --> 00:03:07,580 Et puis je vais parcourir la collection et à chaque fois 60 00:03:07,780 --> 00:03:09,470 ajouter dans la collection le résultat. 61 00:03:09,670 --> 00:03:12,280 " On peut écrire tout ça et c'est complètement équivalent à ça. 62 00:03:12,660 --> 00:03:14,310 A vous de choisir votre camp: est-ce que vous voulez 63 00:03:14,510 --> 00:03:16,750 écrire quelque chose de simple ou quelque chose de plus compliqué? 64 00:03:17,200 --> 00:03:18,730 En gros, la question est là. 65 00:03:19,340 --> 00:03:21,820 Personnellement, je préfère clairement cette première solution. 66 00:03:24,770 --> 00:03:26,750 Il existe dans la hiérarchie des collections Pharo 67 00:03:28,460 --> 00:03:30,510 quelque chose de fondamental, c'est que toutes les 68 00:03:30,710 --> 00:03:32,940 collections sont polymorphiques et héritent de la classe 69 00:03:33,140 --> 00:03:34,800 collection et on a une API commune. 70 00:03:35,440 --> 00:03:38,510 L'avantage, c'est que les itérateurs aussi vont 71 00:03:39,000 --> 00:03:42,760 fonctionner sur la plupart des collections. 72 00:03:46,260 --> 00:03:48,270 L'idée, c'est vraiment de penser objet. 73 00:03:48,470 --> 00:03:51,200 On demande à la collection d'itérer sur ces éléments pour 74 00:03:51,400 --> 00:03:52,870 nous, puisque on ne sait pas si on est en train de 75 00:03:53,070 --> 00:03:56,290 manipuler un dictionnaire ou on n'a pas envie de savoir 76 00:03:56,490 --> 00:03:58,590 comment sont représentées les clefs, les valeurs, et cetera. 77 00:03:59,550 --> 00:04:02,470 Donc on va vraiment demander "Collection, parcours-toi et 78 00:04:02,670 --> 00:04:04,310 applique ce traitement sur tes éléments. 79 00:04:06,810 --> 00:04:07,570 " 80 00:04:07,770 --> 00:04:09,810 Il existe beaucoup d'itérateurs qui vont permettre de faire ça. 81 00:04:10,040 --> 00:04:11,380 On a vu le do, le collect. 82 00:04:11,610 --> 00:04:14,600 On en a d'autres: select, rejet, detect, et caetera, on 83 00:04:14,800 --> 00:04:17,630 va en voir quelques uns dans la suite de cette séquence. 84 00:04:19,680 --> 00:04:23,530 Le do, c'est l'itérateur le plus simple qui permet de 85 00:04:23,730 --> 00:04:25,450 parcourir chacun des éléments de ma collection et je les 86 00:04:25,650 --> 00:04:28,100 affiche ici dans le Transcript. Celui-là, on l'a déjà vu 87 00:04:28,300 --> 00:04:30,130 plein de fois dans les autres exemples. 88 00:04:31,000 --> 00:04:33,280 Un nouvel itérateur qui est select. 89 00:04:33,720 --> 00:04:36,940 Je veux récupérer tous les éléments de la collection qui 90 00:04:37,140 --> 00:04:39,250 répondent à un critère donné. 91 00:04:39,450 --> 00:04:41,870 Ici, je veux tous les éléments impairs de la collection. 92 00:04:42,300 --> 00:04:46,800 J'envoie "select" à la collection, je passe un block et à 93 00:04:47,000 --> 00:04:50,610 chaque fois que ce bloc va s'évaluer à "Vrai", l'élément 94 00:04:50,810 --> 00:04:52,600 en question sera ajouté dans la collection résultats. 95 00:04:55,320 --> 00:04:58,190 C'est complètement équivalent à select: odd. 96 00:04:59,530 --> 00:05:02,790 Quand j'ai un un bloc ici ou ce qui constitue seulement 97 00:05:03,000 --> 00:05:05,940 un envoi de message à l'élément de la collection, le 98 00:05:06,140 --> 00:05:08,370 paramètre du block, je peux mettre directement sous la 99 00:05:08,570 --> 00:05:11,120 forme d'un symbole le nom du message à envoyer. 100 00:05:11,320 --> 00:05:15,380 C'est encore plus concis et ça ne marche que pour les messages unaires. 101 00:05:18,020 --> 00:05:20,510 On peut utiliser d'autres types d'itérateurs comme reject. 102 00:05:21,100 --> 00:05:23,610 Je veux éliminer tous les éléments de la collection qui 103 00:05:23,810 --> 00:05:26,080 sont impairs et donc, dans la collection résultats, il ne 104 00:05:26,280 --> 00:05:27,090 va me rester que des éléments pairs. 105 00:05:28,610 --> 00:05:31,330 Ou alors, je veux faire un detect, je veux détecter le 106 00:05:31,530 --> 00:05:33,620 premier élément de la collection qui répond à un critère 107 00:05:33,820 --> 00:05:37,350 donné pour lequel l'évaluation du bloc va être vraie. 108 00:05:37,550 --> 00:05:40,440 Je veux le premier élément de la collection qui est impair, donc 11. 109 00:05:42,720 --> 00:05:45,760 Dans certains cas, on veut détecter le premier élément de 110 00:05:45,960 --> 00:05:47,660 la collection qui répond à un critère, mais s'il n'y en a 111 00:05:47,860 --> 00:05:50,120 pas alors on aimerait bien avoir une valeur par défaut. 112 00:05:50,590 --> 00:05:54,090 C'est "detect: ifNone", donc s'il n'y a aucun élément c'est 113 00:05:54,940 --> 00:05:57,680 l'évaluation de ce block là qui va être effectuée, et 114 00:05:57,880 --> 00:05:58,720 donc ça va bien nous renvoyer 0. 115 00:06:00,190 --> 00:06:03,410 Il y a d'autres itérateurs encore qui vont encore 116 00:06:03,610 --> 00:06:06,000 faciliter la vie du programmeur. Par exemple, le "anySatisfy". 117 00:06:06,180 --> 00:06:09,920 Est-ce qu'il existe dans la collection un élément qui 118 00:06:10,120 --> 00:06:11,830 répond à ce critère-là? Je veux tous les éléments d'une 119 00:06:12,030 --> 00:06:13,460 collection qui répondent à un critère donné. 120 00:06:13,660 --> 00:06:16,210 Je veux parcourir la collection à l'envers en partant de 121 00:06:16,410 --> 00:06:19,040 la fin vers le début. Je veux parcourir la collection en 122 00:06:19,240 --> 00:06:21,510 ayant un curseur d'index. Je veux parcourir les éléments 123 00:06:21,710 --> 00:06:22,470 de la collection 2 à 2. 124 00:06:22,670 --> 00:06:24,300 Je veux parcourir toutes les permutations circulaires 125 00:06:24,500 --> 00:06:25,630 possibles des éléments d'une collection. 126 00:06:26,770 --> 00:06:29,640 Il y en a beaucoup des itérateurs et on peut en 127 00:06:29,840 --> 00:06:33,330 construire des nouveaux, d'ailleurs. Ici, je veux 128 00:06:33,530 --> 00:06:37,680 parcourir une collection 1, 2, 3 couplée avec une deuxième collection. 129 00:06:39,000 --> 00:06:42,570 Dans mon bloc do, j'aurai x et y, 2 paramètres. 130 00:06:43,190 --> 00:06:46,590 Le premier x, ça sera un élément de la première 131 00:06:46,790 --> 00:06:48,740 collection, et y un élément de la deuxième collection. 132 00:06:49,160 --> 00:06:51,420 Donc je vais pouvoir multiplier ces éléments entre eux. 133 00:06:51,620 --> 00:06:52,840 On obtient bien 10, 40 et 90. 134 00:06:55,050 --> 00:06:57,100 Et bien évidemment, il faut absolument que les 2 135 00:06:57,300 --> 00:06:58,790 collections aient la même taille avec cet itérateur-là. 136 00:07:01,510 --> 00:07:04,200 On peut avoir d'autres types de parcours. 137 00:07:04,400 --> 00:07:06,840 Ici, j'utilise le "do separatedBy". 138 00:07:08,590 --> 00:07:10,640 J'ai une collection, je vais parcourir chacun des 139 00:07:10,840 --> 00:07:14,140 éléments et à chaque fois que j'ai parcouru un élément, 140 00:07:14,340 --> 00:07:17,670 je vais évaluer un bloc ici qui correspond à l'affichage d'une virgule. 141 00:07:18,040 --> 00:07:21,140 Ça va me permettre de parcourir le A, afficher une 142 00:07:21,340 --> 00:07:23,210 virgule, afficher le B, afficher une virgule, afficher le C. 143 00:07:23,410 --> 00:07:26,340 A chaque fois entre chaque élément, je vais avoir effectué une action. 144 00:07:29,000 --> 00:07:32,350 Ici, j'ai un itérateur qui est "GroupBy", qui me permet 145 00:07:32,550 --> 00:07:37,510 de grouper les éléments d'une collection en fonction d'un critère. 146 00:07:39,040 --> 00:07:41,610 J'envoie ce message à la collection 1, 2, 3, 4, 5, 6, 7 147 00:07:41,810 --> 00:07:45,330 ici et je récupère en résultat un dictionnaire. 148 00:07:47,170 --> 00:07:50,010 Donc tous les éléments qui ont répondu "Faux" à ce 149 00:07:50,210 --> 00:07:52,580 critère, le critère c'était "even" c'est les éléments 150 00:07:52,780 --> 00:07:54,740 pairs, donc on voit que ça contient bien une collection 151 00:07:54,940 --> 00:07:57,300 de tous les éléments impairs. Et tout ce qui a répondu 152 00:07:57,500 --> 00:07:59,000 "Vrai", c'est tous les éléments 153 00:08:02,370 --> 00:08:03,130 pairs. 154 00:08:03,330 --> 00:08:04,290 Quand on a une collection souvent quand on fait des 155 00:08:04,490 --> 00:08:06,670 calculs on a tendance à imbriquer des collections dans 156 00:08:06,870 --> 00:08:10,510 des collection, et on se retrouve avec des niveaux d'imbrication 157 00:08:10,710 --> 00:08:11,470 qui peuvent être importants. 158 00:08:11,960 --> 00:08:15,190 Ici, vous avez un exemple construit à la main où on a des 159 00:08:15,390 --> 00:08:17,310 collections qui sont imbriquées dans des collections. 160 00:08:18,070 --> 00:08:20,890 Ce qu'on aimerait c'est arriver à aplatir la collection, 161 00:08:21,090 --> 00:08:22,180 à mettre tous les éléments au même niveau. 162 00:08:23,180 --> 00:08:27,690 Pour ça, on a quelque chose de facile en Pharo, on a un 163 00:08:27,890 --> 00:08:28,830 itérateur qui s'appelle "flatCollect". 164 00:08:29,030 --> 00:08:30,800 C'est-à-dire que je vais parcourir les éléments et 165 00:08:31,000 --> 00:08:32,850 construire une nouvelle collection dans laquelle j'ai tout aplati. 166 00:08:35,400 --> 00:08:38,710 Donc on obtient bien la collection 1, 2, 3, 4, 5, 6 dans 167 00:08:38,910 --> 00:08:40,490 laquelle on a enlevé tous les niveaux d'imbrication. 168 00:08:45,240 --> 00:08:47,480 Le secret, ce n'est pas de vous présenter tous les 169 00:08:47,680 --> 00:08:50,130 itérateurs disponibles dans Pharo, ce serait long et fastidieux. 170 00:08:50,330 --> 00:08:52,170 L'idée, c'est vraiment de vous montrer qu'il en existe 171 00:08:52,370 --> 00:08:54,620 plein et que vous pouvez découvrir vraiment les vôtres en 172 00:08:54,820 --> 00:08:57,500 allant lire les méthodes qui existent sur les classes des 173 00:08:57,700 --> 00:08:59,460 collections, en allant découvrir ces méthodes. 174 00:09:00,400 --> 00:09:02,240 Un exemple simple, c'est de commencer par les itérateurs 175 00:09:02,440 --> 00:09:04,260 que vous connaissez. Par exemple, se poser la question 176 00:09:04,460 --> 00:09:06,420 "Comment est-ce qu'est implémenté le do?" 177 00:09:06,620 --> 00:09:08,850 Je cherche le do dans la hiérarchie des collections, je 178 00:09:09,050 --> 00:09:09,840 vais voir qu'il est implémenté dans 179 00:09:10,040 --> 00:09:13,420 "SequenceableCollection", toutes les collections séquençables. 180 00:09:14,220 --> 00:09:16,580 Donc la méthode "do", elle prend en paramètre un block. 181 00:09:16,900 --> 00:09:19,180 Et voici l'implémentation de cette collection. 182 00:09:19,630 --> 00:09:23,190 1 a la taille de la collection do et j'ai un block. 183 00:09:23,540 --> 00:09:27,000 Je vais évaluer le block qui est passé en paramètre en 184 00:09:27,200 --> 00:09:29,310 lui passant l'élément à l'indice I. 185 00:09:30,330 --> 00:09:31,090 Donc c'est tout simple. 186 00:09:32,350 --> 00:09:36,550 Les itérateurs sont extrêmement puissants en Pharo, comme 187 00:09:36,750 --> 00:09:40,650 on a pu le voir. Toutes les collections supportent ces 188 00:09:40,850 --> 00:09:41,850 itérateurs de façon polymorphique. 189 00:09:42,350 --> 00:09:45,470 Du point de vue programmeur, on utilise des itérateurs et 190 00:09:45,670 --> 00:09:47,290 puis c'est chacune des classes de collections qui vont 191 00:09:47,490 --> 00:09:50,500 les implémenter de façon adéquate par rapport à la 192 00:09:50,700 --> 00:09:51,550 collection qu'ils représentent. 193 00:09:52,880 --> 00:09:55,450 On peut en définir des nouveaux, extrêmement intéressants. 194 00:09:55,650 --> 00:09:57,520 Je peux définir mes propres itérateurs si j'en ai envie 195 00:09:57,720 --> 00:09:58,700 sur les classes de collections. 196 00:10:00,550 --> 00:10:02,010 Il y a une subtilité. 197 00:10:02,210 --> 00:10:05,310 Pour ceux qui connaissent le Design pattern iterator, en 198 00:10:05,510 --> 00:10:08,770 fait la différence c'est que le développeur ne contrôle 199 00:10:09,000 --> 00:10:10,540 pas quand il passe à l'élément suivant. 200 00:10:10,740 --> 00:10:14,260 C'est la collection, qui en interne, décide de passer à l'élément suivant. 201 00:10:14,460 --> 00:10:17,820 On n'envoie pas explicitement le message "next" à l'itérateur. 202 00:10:18,020 --> 00:10:20,120 C'est une subtilité pour ceux qui connaissent le Design pattern iterator. 203 00:10:21,690 --> 00:10:26,360 En résumé, les itérateurs sont vraiment puissants et 204 00:10:27,040 --> 00:10:30,810 un allié fort du programmeur qui vont faciliter l'écriture des programmes. 205 00:10:31,010 --> 00:10:33,470 On l'a vu, ça permet d'écrire du code concis, simple et 206 00:10:33,670 --> 00:10:38,210 élégant et ça permet de garantir l' 207 00:10:38,410 --> 00:10:39,880 encapsulation des données au sein d'une collection.