我以为我了解地图然而以下结果我不明白。我知道为什么会这样,我只是不知道它是怎么回事。
问题是@array的内容正在发生变化,因为在$_
调用期间_do_stuff_to_file
正在重置。所以当我希望它是here: \nhere:\n
时,打印的是here: donkie\nhere: kong\n
。
注意:这不是经过测试的代码。这就是我记得从实验室看到的东西。为什么@array
的内容会发生变化?
如果我在$_
返回1之前将$f
设置为_some_func
。然后阵列仍然完好无损。
这是一个示例程序,用于说明我所看到的内容:
my @array = ("donkie", "kong");
map { push @junk, _some_func('blah', $_); } @array;
if (join ('', @junk) !~ /0/)
{ # for example sake this is always true since return 1 from _some_func.
print map { "here: $_\n"; } @array;
}
sub _some_func
{ # for example sake, lets say $f always exists as a file.
my $j = shift;
my $f = shift;
return 0 if !open(FILE, "< $f");
close FILE;
_do_stuff_to_file($f);
return 1;
}
sub _do_stuff_to_file
{
my $f = shift;
open(IN, "< $f");
open(OUT, "> $f.new");
while (<IN>)
{
print OUT;
}
close IN;
close OUT;
}
答案 0 :(得分:9)
Perl中的许多函数都使用默认变量$_
。其中包括map
和readline运算符<>
。与foreach
类似,map
使循环变量成为它处理的列表的每个元素的别名。发生了什么事情就是这一行:
while (<IN>)
当$_
的别名生效时,正在分配给map
。这是使用$_
(或任何其他全局变量)的问题之一 - 远距离的奇怪动作。如果您要使用$_
,请先将其本地化:
local $_;
while (<IN>)
...
或者,改为使用词法变量:
while (my $line = <IN>)
答案 1 :(得分:4)
修改$ _将改变你的初始数组,因为$ _是当前元素的别名。您的代码应如下所示:
my @array = ("donkie", "kong");
my @junk=map {_some_func('blah', $_) } @array;
if (join ('', @junk) !~ /0/)
{ # for example sake this is always true since return 1 from _some_func.
print map { "here: $_\n"; } @array;
}
sub _some_func
{ # for example sake, lets say $f always exists as a file.
my $j = shift;
my $f = shift;
return 0 if !-e $f;
_do_stuff_to_file($f);
return 1;
}
sub _do_stuff_to_file
{
my $f = shift;
local $_;
open(IN, "<",$f);
open(OUT, ">", "$f.new");
while (<IN>)
{
print OUT;
}
close IN;
close OUT;
}
P.S。 map返回具有相同元素数的数组(如果从块返回标量)。 grep只返回块为true的元素。
答案 2 :(得分:4)
大多数设置$ _的东西都隐含了别名,所以不会导致这个问题;例外是while (<filehandle>)
。虽然你可以本地化$ _(理想情况下是my $_;
),但最好永远不要让隐式设置$ _。请改为while ( my $line = <filehandle> )
。 (特殊的隐式定义的()仍然会发生。)
答案 3 :(得分:0)
我赞同亚历山大和迈克尔的答案:_do_stuff_to_file()
正在改变$_
的价值。
正如map
$_
的上下文只是存储映射元素的名称一样,数组也会更改。
Alexander和Michael建议更改_do_stuff_to_file()
,以免影响$_
值。这是本地化特殊变量(例如$_
)以避免扰乱外部范围的好习惯。
这是一个避免触及该功能的替代解决方案:在调用函数之前通过本地化来“中断”地图块内的链接:
map { my $x=$_; local $_; push @junk, _some_func('blah', $x); } @array;
或更多遵循共同风格:
@junk = map { my $x=$_; local $_; _some_func('blah', $x) } @array;