2009-06-18 6 views
7

Donc, je ne l'ai jamais su et je voudrais avoir quelques éclaircissements dessus. Je sais que si vous faites

foreach (@list){ 

si vous modifiez $ _ dans cette boucle, cela affectera les données réelles. Mais, je ne savais pas que si vous le faisiez

foreach my $var1 (@list){ 

Si vous changiez $ var1 dans la boucle, cela changerait les données réelles. : -/Donc, existe-t-il un moyen de boucler @list mais garder la variable en lecture seule, ou une copie qui, si elle est modifiée, ne changera pas la valeur de @list?

Répondre

10

$var est aliasé à chaque élément à son tour.

Voir http://perldoc.perl.org/perlsyn.html#Foreach-Loops

Si un élément de LIST est une lvalue, vous pouvez le modifier en modifiant VAR dans la boucle. Inversement, si un élément de LIST n'est PAS une lvalue, toute tentative de modification de cet élément échouera. En d'autres termes, la variable d'index de boucle foreach est un alias implicite pour chaque élément dans la liste que vous bouclez.

8

plus simple est juste de le copier:

foreach my $var1 (@list) { 
    my $var1_scratch = $var1; 

ou

foreach my $var1 (map $_, @list) { 

Mais si $ var1 est une référence, var1_scratch $ sera une référence à la même chose. Pour être vraiment sûr, vous auriez à utiliser quelque chose comme Storable :: dclone faire une copie complète:

foreach my $var1 (@{ Storable::dclone(\@list) }) { 
} 

(non testé). Ensuite, vous devriez pouvoir changer $ var1 en toute sécurité. Mais cela pourrait être coûteux si @list est une grande infrastructure de données.

+1

Attention, $ var1 est pas une référence, mais un alias. Voir la différence: my $ var2 = \ $ list [0]; imprime "$ var2 \ n"; affiche SCALAR (0x8171880) – wazoox

+3

@ wazoox: Il voulait dire si $ var1 contenait une référence, par ex. si @list contenait un tas de références HASH. Même si vous cassez l'alias, vous avez toujours une autre copie d'une référence à la même chose. –

1

Je ne sais pas comment forcer la variable à être valeur par-à-la place de référence par référence dans l'instruction foreach elle-même. Vous pouvez cependant copier la valeur de $_.

#!/usr/perl/bin 
use warnings; 
use strict; 

use Data::Dumper; 

my @data = (1, 2, 3, 4); 

print "Before:\n", Dumper(\@data), "\n\n\n"; 

foreach (@data) { 
    my $v = $_; 
    $v++; 
} 

print "After:\n", Dumper(\@data), "\n\n\n"; 

__END__ 
+2

Où vous dites 'my $ v = shift ', vous voulez dire' my $ v = $ _; '. – dave4420

+0

@Dave Hinton a corrigé l'erreur. @Sukotto essayez d'imprimer $ v dans le corps de votre boucle comme vous l'avez écrit à l'origine pour voir votre erreur. –

+0

Oui, je voulais dire 'my $ v = $ _; ':-( – Sukotto

4

C'est un alias, pas une référence. Si vous voulez créer vos propres alias (en dehors de), vous pouvez utiliser Data::Alias.

+1

Umm ... +0. C'est +1 pour la clarification alias/référence et -1 pour donner des instructions pour faire le contraire de ce que le PO a demandé (Création d'un alias par opposition à l'écrasement d'alias.) –

+0

@Michael Carman ysth avait déjà donné la bonne réponse, je soulignais simplement qu'il est possible de créer vos propres alias –

2

La seule différence entre ces boucles:

foreach (@array) { ... } 
foreach my $var (@array) { ... } 

est la variable de boucle. L'aliasing est une fonction de foreach, pas la variable implicite $_. Notez qu'il s'agit d'un alias (un autre nom pour la même chose) et non un référence (un pointeur vers une chose).

Dans le simple (et commune) cas, vous pouvez briser l'aliasing en faisant une copie:

foreach my $var (@array) { 
    my $copy = $var; 
    # do something that changes $copy 
} 

Cela fonctionne pour les valeurs scalaires ordinaires.Pour les références (ou objets), vous devrez faire une copie en profondeur en utilisant Storable ou Clone, ce qui pourrait être coûteux. Les variables liées sont également problématiques, perlsyn recommande d'éviter complètement la situation.

+0

ou Cloner :: Plus ou Cloner :: Rapide – ysth

+0

ou faites une copie dans le foreach avec pour mon $ var (() = @array) – MkV

0

Faire une copie de @list dans l'instruction:

foreach my $var1 (() = @list) { 
    # modify $var without modifying @list here 
} 
Questions connexes