C'est exact mais peut-être un peu rapide... Je vais essayer de détailler un peu plus. L'objectif ici est de définir une table de hachage, %wanted, permettant d'optimiser (on verra plus tard comment) la lecture des lignes en se limitant à celles effectivement utiles dans notre cas. Ces dernières sont celles commençant par CLASS, SCHED, SCHEDPOOL et INCLUDE, plus POOL qu'on utilisera comme valeur de substitution pour SCHEDPOOL lorsque ce dernier vaut *NULL*.
Allons-y petit à petit, en réétablissant le contexte :
En ligne 1 ci-dessus, on déclare la liste @wanted, et on l'initialise avec les valeurs 'CLASS', 'SCHED', 'SCHEDPOOL', et 'INCLUDE'. On aurait pu écrire
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 my @wanted = qw(CLASS SCHED SCHEDPOOL INCLUDE); my @needed = qw(POOL); my %needed = map { $_ => 1 } @wanted, @needed;
mais l'utilisation de l'opérateur qw évite de s'encombrer des apostrophes et des virgules, et on gagne en lisibilité.
Code : Sélectionner tout - Visualiser dans une fenêtre à part my @wanted = ('CLASS', 'SCHED', 'SCHEDPOOL', 'INCLUDE');
Que représente cette variable @wanted ? C'est la liste des clés que l'on veut faire apparaître dans les lignes générées. On s'en sert ligne 3 et plus tard dans le programme, donc selon le principe 'DRY' (Don't Repeat Yourself -- ne vous répétez pas), on la définit une fois pour toutes, et surtout une seule fois.
La variable @needed représente la liste des clés dont on a besoin en plus de celles que l'on va afficher. A ce stade elle ne contient qu'une seule valeur, mais peut-être y en aura-t-il d'autres dans le futur si on est amené à faire évoluer le programme... Ça ne coûte pas grand chose d'en faire une liste. Je reconnais par contre qu'elle est peut-être mal nommée, en relation avec la table de hachage %needed. Je l'avais introduite surtout pour faire réaliser aux lecteurs qui n'en seraient pas forcément conscients qu'en Perl on peut définir et faire coexister des entités de type différent portant le même nom : la liste @foobar, la table de hachage %foobar, le scalaire $foobar, la fonction &foobar, le glob *foobar. Quoi qu'il en soit, la table de hachage %needed vise à représenter les clés qu'on doit lire, alors que @needed liste les clés qu'on doit lire en plus de celles figurant dans @wanted... Petite maladresse donc, potentiellement source de confusion, que l'on peut corriger en rebaptisant @needed en @extra :
Intéressons nous maintenant à cette fameuse ligne 3. Clairement, on déclare la table de hachage %needed, et on va l'initialiser en invoquant l'opérateur map. Cet opérateur peut être invoqué de deux manières : map EXPR, LIST ou map BLOCK LIST. J'ai tendance à utiliser systématiquement la seconde forme, que je trouve plus claire, mais c'est affaire de goût. Ici BLOCK est { $_ => 1 }, et LIST est @wanted, @extra. Il est important de comprendre que lorsque des listes figurent dans une liste, elles sont aplaties pour produire la liste finale. Autrement dit, les expressions suivantes sont équivalentes :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3 my @wanted = qw(CLASS SCHED SCHEDPOOL INCLUDE); my @extra = qw(POOL); my %needed = map { $_ => 1 } @wanted, @extra;
au final elles se résolvent toutes identiquement après aplatissement à la liste@wanted, @extra
(@wanted, @extra)
@wanted, ('POOL')
@wanted, 'POOL'
'CLASS', ('SCHED', ('SCHEDPOOL', ('INCLUDE', ('POOL'))))
((((('CLASS'), 'SCHED'), 'SCHEDPOOL'), 'INCLUDE'), 'POOL')
et c'est sur cette liste que map va opérer.'CLASS', 'SCHED', 'SCHEDPOOL', 'INCLUDE', 'POOL'
Que fait map en l'occurrence ? Pour chaque élément de la liste il va évaluer le BLOCK { $_ => 1 } et construire la liste des résultats obtenus. A l'intérieur du BLOCK, $_ est un alias sur l'élément courant de la liste, celui sur lequel on travaille. L'expression $_ => 1 construit la liste ($_, 1), et c'est la valeur de retour du BLOCK. Map va donc construire la liste de listes suivante :
qui du fait de l'aplatissement évoqué ci-dessus est en fait('CLASS', 1), ('SCHED', 1), ('SCHEDPOOL', 1), ('INCLUDE', 1), ('POOL', 1)
Que se passe-t-il lorsqu'on affecte cette liste à la table de hachage %needed ? La liste est décomposée en couples consécutifs (clé, valeur) et pour chaque couple on affecte valeur à la position clé dans la table, c'est à dire qu'on exécute $h{clé} = valeur. Autrement dit, tout se passe comme si on avait effectué la séquence d'opérations suivantes :'CLASS', 1, 'SCHED', 1, 'SCHEDPOOL', 1, 'INCLUDE', 1, 'POOL', 1
(Noter que si une clé figure plusieurs fois dans la liste, c'est la dernière valeur associée qui l'emporte...)$h{'CLASS'} = 1;
$h{'SCHED'} = 1;
$h{'SCHEDPOOL'} = 1;
$h{'INCLUDE'} = 1;
$h{'POOL'} = 1;
En fait cette fameuse ligne 3 est équivalente à
ou à
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 my %needed; foreach my $cle (@wanted, @extra) { $needed{$cle} = 1; }
ou encore à
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4 my %needed; foreach (@wanted, @extra) { $needed{$_} = 1; }
(on aurait pu utiliser for en lieu et place de foreach ci-dessus). Peut-être ces formes sont-elles plus claires pour certains. La dernière en particulier paraît plus compacte que notre ligne 3 avec son map. Est-elle pour autant préférable ?
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2 my %needed; $needed{$_} = 1 foreach @wanted, @extra;
A mon avis non, et la raison tient au fait que ces formes séparent la déclaration de la variable %needed de son initialisation. Pour un programmeur expérimenté, il y a quelque chose de troublant, de vaguement malsain et inquiétant, à déclarer une variable qui reste -- aussi brièvement que cela soit -- non initialisée. L'initialisation immédiate par map évite ce malaise. Ça paraît peut être un détail comme ça, mais ces détails finissent par faire une grosse différence sur le long terme. Par ailleurs, c'est un idiome (ou si vous préférez, une manière de coder) parfaitement reconnaissable pour un programmeur Perl ayant un peu de bouteille. De plus cet idiome est bien connu du compilateur perl lui même, qui est susceptible de l'optimiser.
Notons cependant que les boucles ci-dessus sont parfaitement légitimes lorsque la table %needed existe déjà, par exemple dans la situation suivante :
Ici en ligne 3 on initialise la table de hachage %needed comme précedemment, tandis qu'en ligne 5, on vient compléter (ou mettre à jour) cette table : les seules entrées affectées sont celles dont les clés figurent dans @more, et l'utilisation d'une boucle est alors tout à fait adaptée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6 my @wanted = qw(CLASS SCHED SCHEDPOOL INCLUDE); my @extra = qw(POOL); my %needed = map { $_ => 1 } @wanted, @extra; ... my @more = ...; $needed{$_} = 1 foreach @more;
Bon, ça suffit peut être pour aujourd'hui ... Je reviendrai plus tard sur les autres points.
Au fait, n'hésitez pas à marquer votre appréciation (ou votre dégoût ) pour ce message en utilisant les boutons (ou ) ci dessous à droite... Ce genre de rédaction prend pas mal de temps, et un peu de feedback des lecteurs est toujours bienvenu !
Partager