.-----------[MinDKinD100\#100.08]------- ----------. [M]ind[K]ind #100 | ; | - 35/12/03 | . À la conquete du chameau part 4 | - LastCall_ | aka '------------- ----. | i love perl | | _____ ; | '--------------------- ----- -[again, again n again.]---- ------------' Pour ceux qui avait déjà une base en Perl, les articles précédent de cette série ont été fastoche en partie.. maintenant on va enfin s'amuser avec des choses plus "hard". Pour ceux qui s'amusait en C a faire des pointeurs vont enfin peut-être trouver du plaisir à gosser avec des "références" en Perl, mais d'une manière plus utile que juste passer des paramètres par référence. Pour ceux qui ont peur du compliqué, arrêtez-vous la parce que vous allez voir des choses paranormales avec les références si vous arrivez pas a comprendre le principe de référence, référent et déréférence. References ¯¯¯¯¯¯¯¯¯¯ Lorsqu'on fini par jongler avec les variables, aka leur faire faire vraiment ce que l'on veut faire, et bien les références deviennent intéressantes. Petite mise en garde ici, c'est pas du C mais bien du Perl, donc une référence ce trouve à être véritablement seulement une référence à une variable ou une fonction, c'est pas comme en C où l'on garde simplement la valeur de la case mémoire de la variable pointé. Donc oublier toute ce que vous savez en C avec les soit disant "pointeurs" qui sont en faite que des int, ici en Perl on joue qu'avec des vrais références, donc les trucs à la C ne fonctionne pas (eg : incrémenter une référence). Il existe donc deux types de références : les hard et les soft. Une référence hard est la chose à lequel vous vous attendez réellement d'une référence, et la soft est un genre de "tricks" que la plupart des langages de type "scripting" nous permettent de faire. Voici comment créer une référence hard à partir de la variable meuh et la foutre dans pwet: $pwet = \$meuh; Simple! et pour lui accèder (autrement dit déréférencer une variable scalaire) on fait : print $$pwet; Si vous faite print $pwet; vous allez voir quelque chose comme SCALAR(0x1014072c). C'est ce que Perl retourne lorsqu'on print une référence sur un scalar. Vous pouvez aussi bien sure faire des références des types que vous voulez... $a = \%hash; $b = \@array; Et pour printer $b on fait print @$b. Si vous oubliez de mettre le @, vous allez voir un ARRAY(0x1014072c), même chose pour le hash, si vous oubliez le %, vous allez voir HASH(0xqqchose). Faut donc noter qu'une référence est une variable scalaire, et qui faut la déréférencer de la bonne manière, sinon si on fait print $$b; par exemple on va se retrouver avec le message d'erreur : "Not a SCALAR reference". Par contre $$b[0] est valide, et que $$a{bleh} l'est aussi. On peut aussi déréférencer avec @{} ${} %{}, par exemple @{$b} est valide. Et comme si c'était pas assé, pour pseudo simuler de l'objet, Perl nous permet de rajouter -> pour déréférencer, donc $b->[0] et $a->{bleh} reviennent au même que $$b[0] et $$a{bleh} fou hein? Pour pousser ça encore plus loin (oui tjrs plus loin, encore! wouhou!) on peut faire des références anonymes, qui pointe sur un endroit qui est pas une variable qui existe : $pwet = \"roxor"; print $$pwet; # print roxor! Bon ok, la tlm se demande à quoi ça sert? Pour l'instant rien.. haha mais regarder ça : $pwet = [1, 2, 3]; Avec [] on peut créer une liste anonyme.. c'est tu pas super? Donc $pwet[0] donne 1. Et pis pour en rajouter encore dans ce semblant d'inutilité, voila le hash anonyme : $pwet = { aaa => "ahh!", bbb => "bhh!" }; Cool heh? Bon ok j'vous laisse 2 secondes pour penser a quoi ça sert... .... rappelez vous de mon premier article qui montrait à faire des tableaux à 2 dimensions : @a = ( [ "oui", "non" ], [ "yes", "no" ], [ "dah", "niet" ], ); Ah wow ya des []! Ah wow c'est des références! hehe Et oui avec tout ça on peut s'amuser à faire ce qu'on veut tel que : $hashref = { alouette => ["et ben", "oui", "et encore"] }; Qui se déréférence de la manière suivante : print $hashref->{alouette}->[0]; #print et ben ou encore : print @{$$hashref{alouette}}; # print et benouiet encore ou ben : print ${$$hashref{alouette}}[0]; # print et ben Mais la attention les yeux.. c'est à vous de comprendre le principe et d'après imaginer toute les structures que vous pouvez faire pour épater la galerie et rendre votre code illisible (haha). Si vous sentez le besoin d'assigner un paquet de références sur des variables d'une shoot, les () peuvent aider : @refs = \($a, $b, $c); @refs = (\$a, \$b, \$c); @list = ($a, $b, $c); @refs = \(@list); et ça ça revient à faire : @refs; $refs[0] = \$a; $refs[1] = \$b; $refs[2] = \$c; Donc évidamment pour accèder à $a on fait $$refs[0] print ${$refs[0]}; #print le contenu de $a Mais oh! ATTENTION! ne pas faire $$refs[0], car dans ce cas la ça déréférence $refs au lieu de $refs[0], et donc, ça va utiliser la variable $refs au lieu de @refs (qui sont 2 variables totalement différentes!); Et si votre variable $refs n'est pas une référence ça va chier.. ou pire, vous allez faire une soft référence sans vous en rendre compte (oh horreur!). Les soft références sont le type de référence qui vous permettre de faire marcher des références quand vous êtes dans un état mental précaire sans que vous vous en rendiez compte. Voici un exemple simple : $meuh "heh"; $pwet = "meuh"; print $$pwet; #print heh et tant qu'a faire : print ${"meuh"}; # print heh La magie dans tout ça? Lorsque Perl voit que la variable est une string au lieu d'une référence, y'essaye tout simplement de regarder dans sa lookup table de variables si y'a pas de quoi qui matcherait avec.. dans le cas présent ça fait ${"$pwet"}.. plus simple à comprendre écrit ainsi! ;-) Si jamais vous voulez pas que ça vous arrive, faite use strict 'refs'; au début de votre programme et Perl vous laissera pas faire de référence symboliques (aka référence soft). Pour ceux qui aime faire du n'importe quoi, Perl vous laisse aussi le choix de référencer n'importe quoi. Avec * comme symbole devant la variable.. On peut donc ainsi faire des références de filehandles : $pwetFH = \*FH; Fun! * se trouve à gosser avec la table des symboles de Perl (qui contient les variables), donc c'est comme si on serait un peu plus dessous le capot de Perl, en faite $pwet = \$meuh; revient à faire : $pwet = *meuh{SCALAR}; A vous de trouver une utilité pour ça.. noter qu'on peut remplacer SCALAR($) par ARRAY(@), HASH(%), CODE(&) et GLOB(*). Et oui on peut même référencer du code et on peut faire des affaires plutôt intéressante avec ça : foreach my $twit ("a", "b", "c") { *$twit = sub { return $twit x $_[0] } } Koicé ça fait ça? Et bien ça crée des référence globale (donc sans être obliger d'écrire un identifiant tel que $ ou @ devant la variable pour y accéder, et ce pour a b et c. Dans la sub elle-même ça prend le nom de cette référence la et ça le retourne le nombre de fois qu'on spécifie. ouf. bon ok en clair : a(2) retourne "aa". print a(2); # print "aa" À vous de jouer pour plugger ça dans de quoi d'utile... ;-) Pour ceux qui sont pas capable de rien trouver, voici de quoi de bien simple avec le *, disons que je veux faire une référence vers une sub { print "beurk";} je fais : my $twit = "a"; { *$twit = sub { print "beurk\n" } } a(); Notez les { } qui englobe dans un block l'assignation, pourquoi c'est la? En faite j'ai pas d'explication clair et précise sur le sujet, mais disons que coté table des symboles de Perl si je veux que ça marche mon affaire, je dois le mettre dans un block... bizarre, mais nyways on peut pas faire ce "truc" avec use strict car ça crée une référence symbolique.. Un peu de pratique ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Bon sortez vos sabres laser on va maintenant slicer du code les yeux bandés (les YEUX j'ai dis). Avant de faire le Jedi avec les références on va se dégourdir le padawan avec quelques exercices sur les Array (putain je joue trop a KOTOR) : ------------------------- 8< ------------------------------- >8 --------------- my @crystals; # Affectation de "blue" et "red" dans $crystals[4] et $crystals[5] @crystals[4..5] = qw/blue red/; # Affectation de "yellow" dans $crystals[1] (si vous saviez pas ça la...) $crystals[1] = "yellow"; $crystals[0] = "green"; # Pour chaque élément dans l'array @crystal my $i=0; foreach (@crystals){ # print la valeur de l'array à la position $i print "\$crystals[$i] : $_\n"; $i++; } # print le dernier indice (un indice c'est genre $crystal[$INDICE]) # de l'array @crystals print '$#crystals : ' , $#crystals , "\n"; # print le nombre d'éléments du array @crystals print 'scalar @crystals : ' , scalar @crystals , "\n"; # Change le dernier indice du array à 2, # donc @crystals devient juste @crystals[0..2] $#crystals = 2; $i=0; foreach (@crystals){ print "\$crystals[$i] : $_\n"; $i++; } print '$#crystals : ' , $#crystals , "\n"; print 'scalar @crystals : ' , scalar @crystals , "\n"; ------------------------- 8< ------------------------------- >8 --------------- Bon ça serait le fun que vous essayez vous aussi, mais pour les vedges voici le output: $crystals[0] : green $crystals[1] : yellow $crystals[2] : $crystals[3] : $crystals[4] : blue $crystals[5] : red $#crystals : 5 scalar @crystals : 6 $crystals[0] : green $crystals[1] : yellow $crystals[2] : $#crystals : 2 scalar @crystals : 3 Des exemples en veut tu en vla (avec les Array) ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Vous allez avoir besoin de la force pour passer au travers de la prochaine affaire.. ou plutôt, d'avoir comprit le principe avec les foutues références! ------------------------- 8< ------------------------------- >8 --------------- #!/usr/bin/perl -w use strict; # Créer l'array @b my @b = (1, 2, 3); # Crée l'array @a avec la déréférence de la référence de @b # - Ceci est juste pour démontrer que ça marche, # vous êtes barge si vous faite ça pour vrai - my @a = @{ [@b] }; print '@b : ', @b, "\n"; print '@a : ', @a, "\n"; print "\n-----\n"; # RéInitialiser les arrays @a and @b @a = (); @b = (); # Remplir l'array @b @b = (4, 5, 6); # Copié @b dans @a (copy item par item ici, pas de références) @a = @b; print '@b : ', @b, "\n"; print '@a : ', @a, "\n"; print "\n-----\n"; # RéRéInitialiser @a et créer @c @a = (); my @c = (7, 8, 9); # On empile dans @a l'array @b ou plutôt on empile sa référence push @a, [ @b ]; # again! push @a, [ @c ]; print '@b : ', @b, "\n"; print '@c : ', @c, "\n"; print '@a : ', @a, "\n"; print '$a[0][0] : ', $a[0][0], "\n"; print '@{$a[0]} : ', @{$a[0]}, "\n"; # ATTENTION: Ça ça print RIEN! #print @$a[0]; print "\n-----\n"; # RéRéRéInitialiser @a @a = (); # on va se servir de @a comme un array de reférences # On empile dans @a la référence de l'array @b push @a, \@b; # si @a est vide : $a[0] = \@b; push @a, \@c; # NOTE: \@c fait la même affaire que [ @c ], et supposément plus vite. # par contre il va falloir se servir de [ ] si on veut avoir # quelque chose de temporaire (ou anonyme) push @a, [ split '-', '11-12-13' ]; push @a, [22, 23, 24]; # ATTENTION: Les prochains lignes vont pas marcher, ça prends le premier élément de @b # et le push comme un array ref... # avec use strict ç print: Can't use string ("4") as an ARRAY ref while "strict refs" #push @a, @b; # ATTENTION: ne pas se tromper entre [] et () #push @a, (@b); # Sinon ça va faire la même chose que push @a, $b[0], $b[1], $b[2]... # donc @a va être un array normal (pu de références!) print '@b : ', @b, "\n"; print '@c : ', @c, "\n"; print '@a : ', @a, "\n"; print '$a[0][0] : ', $a[0][0], "\n"; print '@{$a[0]} : ', @{$a[0]}, "\n"; print '@{$a[1]} : ', @{$a[1]}, "\n"; print '@{$a[2]} : ', @{$a[2]}, "\n"; print '@{$a[3]} : ', @{$a[3]}, "\n"; # ATTENTION: Si on assaye d'accèder à un indice non existant #print '@{$a[30]} : ', @{$a[30]}, "\n"; # Ça va printer: Can't use an undefined value as an ARRAY reference # ATTENTION: la ligne suivante print rien #print @$a[0]; print "\n"; ------------------------- 8< ------------------------------- >8 --------------- ouf. Voici le output: @b : 123 @a : 123 ----- @b : 456 @a : 456 ----- @b : 456 @c : 789 @a : ARRAY(0x81016f4)ARRAY(0x810300c) $a[0][0] : 4 @{$a[0]} : 456 ----- @b : 456 @c : 789 @a : ARRAY(0x8101724)ARRAY(0x8103830)ARRAY(0x8103078)ARRAY(0x810303c) $a[0][0] : 4 @{$a[0]} : 456 @{$a[1]} : 789 @{$a[2]} : 111213 @{$a[3]} : 222324 M'enfin maintenant vous savez pratiquement tout les trucs de Jedi qu'on peut faire avec des arrays.. pas pire quand même non? ;-) En passant quand vous voyez ARRAY(0x8101724) c'est parce que vous imprimer la référence (donc pas la bonne affaire) pour fixer ça suffi de référencer. Dans le cas de ARRAY(0x8101724) faut faire un @{}. Des exemples en veut tu en vla d'autres (et on rajoute les Hash) ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Bon maintenant on va mixer des hash et des array ensemble. Pour plus de plaisir, plus de puissance, plus de O_o comme on dit dans le langage des smileys. ------------------------- 8< ------------------------------- >8 --------------- #!/usr/bin/perl -w use strict; # Création du hash %d avec les clefs "alpha", "beta" # qui ont comme valeurs les arrays anonymes [10,11,12] et [20,21,22] my %d = ( "alpha" => [10,11,12], "beta" => [20,21,22], ); # Print la référence du array anonyme [10,11,12] print '$d{alpha} : ' , $d{alpha} , "\n"; # Print l'array anonyme [10,11,12] print '@{$d{alpha}} : ' , @{$d{alpha}} , "\n"; # ATTENTION: Print l'array anonyme [10,11,12] dans un contexte scalar # (donc le nombre d'élément de la liste!) print 'scalar @{$d{alpha}} : ' . @{$d{alpha}} . "\n"; # SOLUTION: utiliser la virgule , pour passer des paramètres à print au # lieu de concaténer les strings avec les . # Ne fonctionne pas, erreur: Global symbol "$d" requires explicit package name #print @$d{alpha}; # print la longueur du array anonyme [10,11,12] print '$#{$d{alpha}} : ', $#{$d{alpha}}, "\n"; # fait la même chose que la ligne ci-dessus, en trop laid. (dark side coders only) print '$#{@{$d{alpha}}} : ', $#{@{$d{alpha}}}; print "\n"; ------------------------- 8< ------------------------------- >8 --------------- Le output: $d{alpha} : ARRAY(0x80f815c) @{$d{alpha}} : 101112 scalar @{$d{alpha}} : 3 $#{$d{alpha}} : 2 $#{@{$d{alpha}}} : 2 Références vers une fonction comme en C? ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯ Petite dernière note : on peut passer par référence à une fonction une variable (waw comme en C), sauf que le truc du $_[0] = bleh; est plus leet. my $a = "unf"; sub meuh { my $refa = shift; print "$$refa\n"; $$refa = "ehh"; } meuh \$a; print $a; donne: unf ehh Ce qui revient a faire avec le truc leet : sub meuh { print "$_[0]\n"; $_[0] = "ehh"; } meuh $a; OUF. Voila ce qui conclu cette partie du tuto du coté des références, ne pensez pas que j'ai pour autant fini de vous parler des hashs et des arrays, y'a plein de fonctions intéressantes qui restent à découvrir à leur sujet. Alors vu que la partie des références était plus bourrée que prévu (finalement ça en fait du stock), je reporte ce que j'Avais prévu (c'est à dire les regex et les packages) à mon prochain article de la série. Que la force du chameau soit avec vous, - LastCall_ .----- - -----------------------. | /MinDKinD100 .---'----------- ---------- ------- ------. '-------- - -------------- -| lastcall@mindkind.org | '-------- - ----------- ------------ -----'