passer des parametres par reference [Perl] - Programmation
Marsh Posté le 27-02-2002 à 09:27:40
alligator421 a écrit a écrit : J'aimerais pouvoir passer des arguments par reference aux fonctions sans utiliser de variables locales dans la fonction meme . par exemple habituellement je fais : sub fonction { my $ma_variable = ${$_[0]}; my @mon_tableau = @{$_[1]}; my %mon_hash = %{$_[2]}; # je veux modifier le hash ........ %{$_[2]} = %mon_hash; } ....... Main : { ... &fonction(\$a,\@b,\%c); ... }; Bon alors ca marche parfaitement bien, mais c'est inefficace parce le my %... = %... cree une copie locale donc il reconstruit tout le truc en entierete et quand c'est du hash of arrays c'est lourd en performance (autant utiliser des variables globales alors). Je n'ai pas envie de manipuler les hash avec une notation directe (efficace mais barbare) du style : ${$_[2]}{${$_[0]}} = "coucou"; alors que : $mon_hash{$ma_variable} = "coucou"; c'est si sympathique Bref j'aimerais utiliser un "raccourci" sans variable locale pour %{$_[2]} |
Salut,
Bah écoute essaie ça (si j'ai bien compris ce que tu veux faire) :
sub fonction
{
my ($var_ref, $table_ref, $hash_ref) = @_;
# Ensuite tu peux accéder aux infos comme ceci
print $$var_ref . "\n"; # pour la string
print $$table_ref[2] . "\n"; # pour le tableau
print $hash_ref->{test} . "\n"; # pour le tableau de référence
}
Marsh Posté le 27-02-2002 à 12:39:04
Voici un exemple (un peu bête mais bon) :
my %Hash;
$Hash{1} = 'un';
$Hash{2} = 'deux';
$Hash{3} = 'trois';
Minable, n'est-ce po ?
Imaginons que tu veuilles rajouter une clé dans ton hash en passant ton hash par référence à une fonction RajouteKey, valà ce que ça donne :
sub RajouteKey {
my ($toto) = @_; # ou = shift;
$toto->{4} = 'quatre';
}
Au retour, tu récupères une 4ème clé à ton hash.
Marsh Posté le 27-02-2002 à 17:37:16
ouh-la je crois que je me suis mal exprime.
>tentacle : non, ce n'est pas imprimer les valeurs des variables, hash, ... etc, qui est problematique, ca, c'est ok.
>aricoh : ce que tu montres la, est valable quand on passe les references des keys a la fonction ; mais ils sont passes comme un tableau comme tu le notes bien par @_ ce qui veut dire que si on passe un deuxieme hash, on ne sait pas recuperer le deuxieme, les fonctions du perl passe tous ses arguments dans un tableau mais sans distinguo et idem au retour d'une fonction.
keys @{$premier_hash} = @_;
%second_hash = {}; !!!!!!!
C'est pour ca que je montrais une fonction de type :
my $ma_variable = ${$_[0]};
my @mon_tableau = @{$_[1]};
my %mon_hash = %{$_[2]};
ce que je voulais savoir c'est comment se debarrasser du "my" d'affectation dans la fonction quand on recupere les arguments.
J'aimerais avoir un "alias" pour %{$_[2]} sans devoir passer par la CREATION d'un hash local.
Je ne sais pas comment le perl est implemente (il doit y avoir un constructeur de copie a la facon c++ qq part) mais d'experience (a mes depens) je sais que :
sub fonction {
my %hash = %{$_[0]};
....
# plusieurs traitement avec le %hash qui est modifie et des keys ajoute
...
# %{$_[0]} != %hash avec des hash of arrays ! (je sais pas pq)
};
c'est pour ca que je suis oblige (?) de mettre la derniere ligne d'affectation :
sub fonction
{
my $ma_variable = ${$_[0]};
my @mon_tableau = @{$_[1]};
my %mon_hash = %{$_[2]};
........
%{$_[2]} = %mon_hash; # c indispensable sinon parfois c'est egal, parfois ca ne l'est pas ...
}
il y a certainement bien quelque chose comme dans le c++
genre :
void fonction (int & a)
{
int b = a;
b = 5;
};
// a = 5
[jfdsdjhfuetppo]--Message édité par alligator421--[/jfdsdjhfuetppo]
Marsh Posté le 27-02-2002 à 17:57:33
[citation]il y a certainement bien quelque chose comme dans le c++
genre :
void fonction (int & a)
{
int b = a;
b = 5;
};
// a = 5
[/citation]
Non mais attends si tu fais ca :
Sub fonction ()
{
$a = shift @_;
$b = $a;
$$b = 5;
}
$test = 3;
fonction (\$test);
// $test = 5
Marsh Posté le 28-02-2002 à 08:17:06
alligator421 a écrit a écrit : ouh-la je crois que je me suis mal exprime. >aricoh : ce que tu montres la, est valable quand on passe les references des keys a la fonction ; mais ils sont passes comme un tableau comme tu le notes bien par @_ ce qui veut dire que si on passe un deuxieme hash, on ne sait pas recuperer le deuxieme |
Je t'assure que si, mon p'tit bonhomme !
Tu peux passer autant de hash que tu veux en paramètres à une fonction, justement en utilisant les références
J'utilise le @_, car ce n'est rien de plus que la liste stockant TOUS les arguments que tu veux passer à ta fonction.
Si tu n'utilises pas les références (je préfère dire pointeurs) et que tu passes 2 hash à une fonction like this :
Fonction(%toto, %pouet)
@_ prend tes deux hash dans sa liste et c'est clair que là, tu ne peux pas retrouver tes deux hash et bosser correctement au sein de ta fonction
Si maintenant, tu utilises des pointeurs, je t'assure que tu peux balancer autant de hash que tu veux à ta fonction, like this :
Fonction(\%toto, \%pouet, \%tronche)
Il suffit juste dans ta fonction de respecter le contexte de liste dans tes variables recevant les paramètres.
Essaie ce petit script :
use strict;
my %toto = qw(1 un 2 deux);
my %pouet = qw(3 trois 4 quatre);
my %truc = qw(5 cinq 6 six);
Ma_Fonction(\%toto, \%pouet, \%truc);
print "cle 1 de toto = $toto{1}\n";
sub Ma_Fonction {
my ($hash1, $hash2, $hash3) = @_;
print "Cle 1 de hash toto = $hash1->{1}\n";
print "Cle 1 de hash pouet = $hash2->{3}\n";
print "Cle 1 de hash truc = $hash3->{5}\n";
# je modifie la clé 1 du hash toto
$hash1->{1} = 'POUETTTT';
}
Essaie et tu verras que tu retrouves bien les clés/valeurs de chacun de tes hash associés aux 3 pointeurs que j'utilise dans la fonction, et tu verras itou que tu peux également modifier leur contenu
Marsh Posté le 28-02-2002 à 09:54:46
alligator421 a écrit a écrit : : c'est pour ca que je suis oblige (?) de mettre la derniere ligne d'affectation : sub fonction { my $ma_variable = ${$_[0]}; my @mon_tableau = @{$_[1]}; my %mon_hash = %{$_[2]}; ........ %{$_[2]} = %mon_hash; # c indispensable sinon parfois c'est egal, parfois ca ne l'est pas ... } |
Qu'est_ce que la liste @_ ???
C'est juste un @rray qui empile toutes les variables que tu lui transmets pour les utiliser ensuite à l'intérieur de ta fonction.
Si tu alimente @_ avec 2 scalaires ($toto, $titi), @_ devient un tableau de 2 éléments scalaires, ici ça roule
Si tu alimente @_ avec 2 tableaux (@pouet, @prout), tes deux tableaux n'en font plus qu'un lorsqu'ils sont transmis à ta fonction
Si tu alimente @_ avec 1 scalaire + 1 tableau + 1 hash, @_ regroupe encore une fois tous ces éléments dans un seul tableau.
Et là, à moins de passer par les pointeurs, ça devient gonflant (mais réalisable) de reconstituer ton tableau et surtout ton hash.
Si donc tu alimente @_ avec 2 hash (%hash1, %hash2), @_ prend les clés et les valeurs de tes deux hash et s'en fait une liste, tu perds la notion de tes deux hash
Si tu alimente @_ en lui communiquant les adresses mémoire de tes hash (\%hash1, %hash2), tu lui transmet en fait les premiers éléments de chaque hash. Comme ta fonction connait maintenant l'emplacement mémoire du 1er élément d'un hash, elle sait le reconstituer, tu peux donc envoyer autant de hash que tu le désires dans ta fonction.
Sachant également que lorsqu'on transmet un hash par référence à une fonction, le hash n'est pas dupliqué en mémoire puisqu'on travaille avec le vrai.
Je me souviens avoir traité un gros hash d'environ 120.000 clés et valeurs, j'ai utilisé 3 fonctions qui transformaient les données du hash et je peux te dire qu'au premier essai, je n'avais pas utilisé les pointeurs, je me suis payé un joli "out of memory", j'ai vite compris d'où venait le bin's
Marsh Posté le 28-02-2002 à 12:49:33
Merci a tous les deux pour vos reponses constructives.
>Tentacle : ok, j'ai bien pris note de tes notations pour "a la C++" mais est ce que ta variable $b qui me parait globale dans ton exemple, existe encore hors de la fonction ? Probalement que oui puisque perl les cree "a la volee" (j'evite les globales au possible, c'est la peste dans les gros scripts). J'essayerai de trouver s'il existe une combine sur base de ton exemple. Il me semble bien que j'avais note qq chose sur www.perldoc.com avec les references $$variable.
>Aricoh : Oui, je suis tout a fait d'accord avec ce que tu as dit. Tu as bien expliquer. le @_ va reprendre toutes les references de ce qu'il lui transmis via la fonction, mais il va tout fusionner, sans distinction de ce que tu y mets, variable, hash, arrays ... etc. C'est pour cela que j'avais precise dans ce cas :
[citation]
keys @{$premier_hash} = @_;
%second_hash = {}; # !!!!!!! dans la fonction elle-meme
[/citation]
Toutes les cles du premier et second hash sont reprises et passees en argument a @_ et le second hash ne sais pas recuperer ses propres cles dans la fonction elle-meme.
Donc je reprends mon premier exemple, j'avais deja mis le passage par des pointeur pour le @_ :
[citation]
sub fonction
{
my $ma_variable = ${$_[0]};
my @mon_tableau = @{$_[1]};
my %mon_hash = %{$_[2]}; # je veux modifier le hash
........
%{$_[2]} = %mon_hash;
}
.......
Main :
{
...
&fonction(\$a,\@b,\%c);
...
};
[/citation]
>&fonction(\$a,\@b,\%c); j'appelle la fonction avec des pointeurs et comme cela on peut recuperer le @_ ordonne, avec : @_ = ($_[0],$_[1],...);
Ce que je veux dire, c'est que ca marche tres bien comme j'ai note mais quand j'ai vu le taux d'utilisation processeur quand on utilise 10 pages de code de fonctions qui tourne dans tous les sens 2 ou 3 hash of arrays en argument avec une bonne trentraine de cles et des arrays de 30 variables c'est
De plus, meme passe avec des pointeurs dans les arguments de fonctions, il me semble que le perl fait une copie locale quand on force une affectation.
sub xxx
{
my %hash = %{$_[0]}; # ici @_ ne comporte qu'1 seul element et pas tous les keys du hash
};
si le hash comporte des cles qui point sur des tableaux et que je modifie ces tableaux comme ceci par exemple :
$hash{"numero3"}[3] = $variablequelconque;
ou bien
push ${$hash{"numero3"}},$variablequelconque;
le changement ne se reflete pas PAS TOUJOURS sur %{$_[0]}
Je vais essayer de retouver un exemple dans 1 de mes fonctions dans un script ou le changement ne s'effectue pas et je suis oblige a la fin de la fonction de mettre : %{$_[0]} = %hash; pour que ce soit OK au retour de la fonction.
On dirait qu'il y a une duplication qui est faite en memoire, pourtant en verifiant les adresses memoires : \$hash == \${$_[0]} quand on les imprime.
C'est comme si les cles du hash etaient convenablement gerees quand on en ajoute ou on en retire et cela se repercute sur le hash passe en argument (donc %{$_[0]}) , mais que les arrays (qui sont pointes par les cles) proprement dit ne le sont pas ...
>Aricoh, manifestement, si tu as eu un out of memory, la gestion de la memoire s'est effectuee sur la pile et non pas sur le tas pour le passage des arguments de fonction, c'est clair
Marsh Posté le 28-02-2002 à 12:54:30
J'utilise pas tt à fait la même syntaxe que toi mais quand tu fais ça dans une fonction :
my %hash = %{$_[0]};
Sauf erreur, tu déclare dans ta routine un hash qui est une copie de celui passé en paramètres ou bien c'est un pointeur ?
J'ai peut être pas capté ce que tu voulais dire en fait
Ce que je sais, c'est que si j'ai un hash à passer en param, je ferais plutôt :
my ($hash) = shift;
ainsi, pas de copie de hash, juste un pointeur
arf
Marsh Posté le 28-02-2002 à 13:40:33
Ah ben justement, c'etait ca la question je crois
my %hash = %{$_[0]};
il semblerait que ca fasse une copie locale (donc sur la pile)
du hash qui est passe avec un pointeur mais avec la copie locale qui pointe sur la meme adresse que le pointeur qui est passe en argument (sauf pour les arrays qui sont contenus, eux sont bien locaux a la fonction)).
zut c pas clair ca
bon disons que les adresses memoires des CLES du %hash soient identiques a %{$_[0]} mais pas les arrays a l'interieur.
J'aimerais trouver un truc qui dise que %hash soit juste 1 lien symbolique pour %{$_[0]} (juste 1 autre nom quoi)
si je retire le my devant le my %hash ... , ca devient juste 1 truc global mais ca m'interesse pas trop
Comment je fais avec l'histoire du shift quand je passe PLUSIEURS hash, arrays, etc ... en meme temps ?
Ca signifie quoi au juste le shift exactement ?
J'aime bien les pointeurs, moa
[jfdsdjhfuetppo]--Message édité par alligator421--[/jfdsdjhfuetppo]
Marsh Posté le 28-02-2002 à 13:54:57
alligator421 a écrit a écrit : Ah ben justement, c'etait ca la question je crois my %hash = %{$_[0]}; il semblerait que ca fasse une copie locale (donc sur la pile) du hash qui est passe avec un pointeur mais avec la copie locale qui pointe sur la meme adresse que le pointeur qui est passe en argument (sauf pour les arrays qui sont contenus, eux sont bien locaux a la fonction)). zut c pas clair ca bon disons que les adresses memoires des CLES du %hash soient identiques a %{$_[0]} mais pas les arrays a l'interieur. J'aimerais trouver un truc qui dise que %hash soit juste 1 lien symbolique pour %{$_[0]} (juste 1 autre nom quoi) si je retire le my devant le my %hash ... , ca devient juste 1 truc global mais ca m'interesse pas trop Comment je fais avec l'histoire du shift quand je passe PLUSIEURS hash, arrays, etc ... en meme temps ? Ca signifie quoi au juste le shift exactement ? J'aime bien les pointeurs, moa |
Le shift sert enlever le premier élément d'un tableau et le retourner.
Si tu as plusieurs arguments tu peux faire ainsi :
my ($a, $b, $c) = @_;
si il n'y a qu'un seul tableau dans le lot, ca va, mais si il y en a 2 par exemple, il ne saura pas quoi faire...
Pour le coup des hash, as-tu essaye de faire ca :
my ($hash_ref) = @_;
et ensuite tu peux faire ce que tu veux sur le hash (et ca reste toujours une reference, donc pas de copie) ainsi :
$hash_ref->{toto}
Marsh Posté le 28-02-2002 à 14:02:27
$toto = shift(@pouet) signifie que $toto reçoit le 1er élément du tableau @pouet. Celui-ci perd son 1er élément, attention à ça
si ton @pouet contenait 10 éléments, il n'en a plus que 9 après la manip (son indice 0 est l'ancien indice 1)
Marsh Posté le 28-02-2002 à 14:03:27
Quand je dis my ($hash) = shift;
shift utilisé seul sous-entend shift(@_)
Marsh Posté le 28-02-2002 à 14:12:16
raaaaah
bon si je prends mon truc initial tout a fait general, ca donnerait quoi alors ?
sub fonction
{
my $ref_variable = $_[0];
my $ref_tableau = $_[1];
my $ref_hash = $_[2]; # je veux modifier le hash (au retour de la fonction)
........
} .......
Main :
{
...
&fonction($a,@b,%c);
...
};
tout simplement ?
t'es sur ? je crois que ca va rien modifier au retour de la fonction parce que c'est un passage des arguments par valeur et pas par reference.
Je sais pas tester maintenant, j'ai pas la machine sous la main.
[citation]
Pour le coup des hash, as-tu essaye de faire ca :
my ($hash_ref) = @_;
[/citation]
ben j'essaierai tout ce qui a ete mis sur ce topic.
mais en fait j'ai deja essaye pleins de trucs mais ca marchait pas trop, quand c 1 hash simple c OK mais hash of arrays, il y a des problemes.
[jfdsdjhfuetppo]--Message édité par alligator421--[/jfdsdjhfuetppo]
Marsh Posté le 28-02-2002 à 14:14:25
alligator421 a écrit a écrit : &fonction($a,@b,%c); |
non attends, tu fais pas ca... mais ça :
&fonction (\$a, \@b, \%c);
Marsh Posté le 28-02-2002 à 14:18:18
Tentacle a écrit a écrit : non attends, tu fais pas ca... mais ça : &fonction (\$a, \@b, \%c); |
j'ai deja essaye si je me souviens bien, mais il m'envoie bouler si je recupere pas dans la fonction en mettant (parce que pointeur)
my $ref_variable = ${$_[0]};
my @ref_tableau = @{$_[1]};
my %ref_hash = %{$_[2]};
Marsh Posté le 28-02-2002 à 14:21:00
Tentacle a écrit a écrit : non attends, tu fais pas ca... mais ça : &fonction (\$a, \@b, \%c); |
le & n'est plus employé avec les dernières releases de Perl, fonction (\$a, \@b, \%c) suffit
Marsh Posté le 28-02-2002 à 14:23:06
alligator421 a écrit a écrit : j'ai deja essaye si je me souviens bien, mais il m'envoie bouler si je recupere pas dans la fonction en mettant (parce que pointeur) my $ref_variable = ${$_[0]}; my @ref_tableau = @{$_[1]}; my %ref_hash = %{$_[2]}; |
Bah normalement le pointeur c'est stockable dans une string, c'est le principe des références donc ça devrait marcher ça :
sub fonction ()
{
my ($a, $b, $c) = @_;
}
&fonction (\$d, \@e, \%f);
Marsh Posté le 28-02-2002 à 14:24:14
[citation]
Pour le coup des hash, as-tu essaye de faire ca :
my ($hash_ref) = @_;
[/citation]
si @_ contient 1 hash, la syntaxe est correcte.
si @_ contient 2 hash, je sais pas sur quoi ton $hash_ref va pointer.
Marsh Posté le 28-02-2002 à 14:29:14
Aricoh a écrit a écrit : si @_ contient 1 hash, la syntaxe est correcte. si @_ contient 2 hash, je sais pas sur quoi ton $hash_ref va pointer. |
Bah faut savoir combien d'arguments on fournit (et dans ton exemple, Perl va gueuler).
Sinon tu fais :
$hash_href = shift;
Marsh Posté le 28-02-2002 à 14:35:04
Aricoh a écrit a écrit : le & n'est plus employé avec les dernières releases de Perl, fonction (\$a, \@b, \%c) suffit |
A subroutine may be called using an explicit & prefix. The & is optional in modern Perl, as are parentheses if the subroutine has been predeclared. The & is not optional when just naming the subroutine, such as when it's used as an argument to defined() or undef(). Nor is it optional when you want to do an indirect subroutine call with a subroutine name or reference using the &$subref() or &{$subref}() constructs, although the $subref->() notation solves that problem. See perlref for more about all that.
Subroutines may be called recursively. If a subroutine is called using the & form, the argument list is optional, and if omitted, no @_ array is set up for the subroutine: the @_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid.
ca vient de : http://www.perldoc.com/perl5.6.1/pod/perlsub.html
Marsh Posté le 28-02-2002 à 14:40:26
Tentacle a écrit a écrit : Bah faut savoir combien d'arguments on fournit (et dans ton exemple, Perl va gueuler). Sinon tu fais : $hash_href = shift; |
C'est généralement ce que je fais
Pour Alligator421, attention aussi à ça :
my ($toto) = shift;
$toto pointe sur 1er élément de @_
si tu oublies les () :
my $toto = shift;
$toto n'est pas un pointeur ici
Marsh Posté le 28-02-2002 à 14:41:10
alligator421 a écrit a écrit : A subroutine may be called using an explicit & prefix. The & is optional in modern Perl, as are parentheses if the subroutine has been predeclared. The & is not optional when just naming the subroutine, such as when it's used as an argument to defined() or undef(). Nor is it optional when you want to do an indirect subroutine call with a subroutine name or reference using the &$subref() or &{$subref}() constructs, although the $subref->() notation solves that problem. See perlref for more about all that. Subroutines may be called recursively. If a subroutine is called using the & form, the argument list is optional, and if omitted, no @_ array is set up for the subroutine: the @_ array at the time of the call is visible to subroutine instead. This is an efficiency mechanism that new users may wish to avoid. ca vient de : http://www.perldoc.com/perl5.6.1/pod/perlsub.html |
bon, j'ai encore dit une connerie, et après ???
Marsh Posté le 28-02-2002 à 14:48:00
Tentacle a écrit a écrit : Bah normalement le pointeur c'est stockable dans une string, c'est le principe des références donc ça devrait marcher ça : sub fonction () { my ($a, $b, $c) = @_; } &fonction (\$d, \@e, \%f); |
D'apres la documentation de www.perldoc.com, effectivement, ca devrait fonctionner comme cela. J'essaierai de nouveau avec mes fameux hash of arrays pour voir ce que ca dit tout ca ...
Marsh Posté le 28-02-2002 à 14:49:28
alligator421 a écrit a écrit : D'apres la documentation de www.perldoc.com, effectivement, ca devrait fonctionner comme cela. J'essaierai de nouveau avec mes fameux hash of arrays pour voir ce que ca dit tout ca ... |
Par contre je savais pas qu'on pouvait faire aussi $a->($toto) pour une ref de fonction... je pensais que c'était que pour les hash, merci
Marsh Posté le 28-02-2002 à 14:49:52
tu vas t'y retrouver avec tout ce qu'on a dit dans l'topic ?
bon courage !
Marsh Posté le 28-02-2002 à 14:54:55
Aricoh a écrit a écrit : C'est généralement ce que je fais Pour Alligator421, attention aussi à ça : my ($toto) = shift; $toto pointe sur 1er élément de @_ si tu oublies les () : my $toto = shift; $toto n'est pas un pointeur ici |
ah bon ?
Je savais meme pas.
Ben euh ... c'est vrai qu'il y a deja des differences (subtiles mais c'est comme l'appel des fonctions )pour les fonctions integrees au langage
undef @array;
undef (@array);
push , pop ... etc
Marsh Posté le 28-02-2002 à 14:56:10
Aricoh a écrit a écrit : tu vas t'y retrouver avec tout ce qu'on a dit dans l'topic ? bon courage ! |
Marsh Posté le 27-02-2002 à 06:41:16
J'aimerais pouvoir passer des arguments par reference aux fonctions sans utiliser de variables locales dans la fonction meme .
par exemple habituellement je fais :
sub fonction
{
my $ma_variable = ${$_[0]};
my @mon_tableau = @{$_[1]};
my %mon_hash = %{$_[2]}; # je veux modifier le hash
........
%{$_[2]} = %mon_hash;
} .......
Main :
{
...
&fonction(\$a,\@b,\%c);
...
};
Bon alors ca marche parfaitement bien, mais c'est inefficace parce le my %... = %... cree une copie locale donc il reconstruit tout le truc en entierete et quand c'est du hash of arrays c'est lourd en performance (autant utiliser des variables globales alors).
Je n'ai pas envie de manipuler les hash avec une notation directe (efficace mais barbare) du style : ${$_[2]}{${$_[0]}} = "coucou";
alors que : $mon_hash{$ma_variable} = "coucou"; c'est si sympathique
Bref j'aimerais utiliser un "raccourci" sans variable locale pour %{$_[2]}