boucle de Perl foreach avec des règles de fermeture de fonction
le code suivant
#!/usr/bin/env perl
use strict;
use warnings;
my @foo = (0,1,2,3,4);
foreach my $i (@foo) {
sub printer {
my $blah = shift @_;
print "$blah-$in";
}
printer("test");
}
ne fait pas ce à quoi je m'attendais.
Ce qui se passe exactement? (Je m'attends à imprimer "test-0ntest-1ntest-2ntest-3ntest-4n")
2 réponses
le problème est que Le sub name {...}
construire ne peut pas être imbriqué comme ça dans un for
boucle.
La raison en est que sub name {...}
signifie vraiment BEGIN {*name = sub {...}}
et pour commencer les blocs sont exécutés dès qu'ils sont analysées. Ainsi, la compilation et la liaison variable du sous-programme se produisent au moment de la compilation, avant que la boucle for n'ait une chance de s'exécuter.
ce que vous voulez faire est de créer un sous-programme anonyme, qui liera ses variables à runtime:
#!/usr/bin/env perl
use strict;
use warnings;
my @foo = (0,1,2,3,4);
foreach my $i (@foo) {
my $printer = sub {
my $blah = shift @_;
print "$blah-$i\n";
};
$printer->("test");
}
impression
test-0
test-1
test-2
test-3
test-4
vraisemblablement dans votre cas d'utilisation réelle, ces fermetures seront chargées dans un tableau ou un hachage de sorte qu'elles puissent être consultées plus tard.
vous pouvez toujours utiliser des identificateurs bareword avec des fermetures, mais vous devez faire un petit travail supplémentaire pour vous assurer que les noms sont visibles au moment de la compilation:
BEGIN {
for my $color (qw(red blue green)) {
no strict 'refs';
*$color = sub {"<font color='$color'>@_</font>"}
}
}
print "Throw the ", red 'ball'; # "Throw the <font color='red'>ball</font>"
la réponse D'Eric Strom est correcte, et probablement ce que vous vouliez voir, mais n'entre pas dans les détails de la reliure.
une brève note sur lexical lifespan: les lexiques sont créés au moment de la compilation et sont en fait disponibles avant même que leur portée soit saisie, comme cet exemple le montre:
my $i;
BEGIN { $i = 42 }
print $i;
par la Suite, quand ils sortent de la portée, ils deviennent indisponibles jusqu'à la prochaine fois, ils sont dans la portée:
print i();
{
my $i;
BEGIN { $i = 42 }
# in the scope of `my $i`, but doesn't actually
# refer to $i, so not a closure over it:
sub i { eval '$i' }
}
print i();
Dans votre code, la fermeture est liée à la première lexicale $i
au moment de la compilation.
Cependant, les boucles foreach sont un peu étranges; tandis que le my $i
crée en fait un lexical, la boucle foreach ne l'utilise pas; au lieu de cela, elle l'aliène à l'une des valeurs en boucle à chaque itération et la restitue ensuite à son état d'origine après la boucle. Votre fermeture est donc la seule chose qui renvoie au lexique original $i
.
Une légère variation montre plus de complexité:
foreach (@foo) {
my $i = $_;
sub printer {
my $blah = shift @_;
print "$blah-$i\n";
}
printer("test");
}
Ici, l'original $i
est créé au moment de la compilation et de la fermeture se lie à la première itération de la boucle fixe, mais la deuxième itération de la boucle crée un nouveau $i
sans lien avec la fermeture.