2010-08-21 101 views
7

我以为我理解地图,但以下结果,我不明白。我知道为什么会发生,我只是不知道它是如何发生的。

问题是@array的内容正在改变,因为在调用_do_stuff_to_file期间正在重置$_。所以当我预计它是here: donkie\nhere: kong\n时,打印的是here: \nhere:\n

说明:这是未经测试的代码。这正是我记得从实验室看到的。为什么@array的内容有变化?

如果我将$_设置为$f,然后再从_some_func返回1。然后数组仍然完好无损。

下面是一个例子程序来说明我所看到的:

​​
+3

您的问题已经包含了自己的答案 - 您已经解释了它是如何发生的。你是否想改变你的问题到“我该如何避免让东西暴露$ _'?因为那是我们可以回答的问题(而且迈克尔卡尔曼已经有了) – hobbs 2010-08-21 22:02:05

+0

我同意hobbs提出的问题标题@yesterday Could you更改它? – dolmen 2010-08-23 10:03:10

回答

9

在Perl的许多功能使用默认的变量$_。其中有map和readline运算符<>。与foreach一样,map使循环变量成为它处理的列表的每个元素的别名。发生了什么事的是,这条线:

while (<IN>) 

被分配到$_map的混叠生效。这是使用$_(或任何其他全局变量)的问题之一 - 远处的奇怪动作。如果你要使用$_,第一定位吧:

local $_; 
while (<IN>) 
... 

或者,改为使用词法变量:

while (my $line = <IN>) 
+0

但为什么使用$ _更改数组的内容?isnt $ _只是一个变量,它是数组当前值的副本? – user318747 2010-08-21 22:02:02

+0

好吧我现在看到了,谢谢。 – user318747 2010-08-21 22:04:12

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只返回block为true的元素。

+1

列表'map'返回不一定具有与其输入相同数量的元素,例如'@doubled = map {$ _,$ _}(1..5)' – 2010-08-22 13:45:55

0

我同意Alexander和Michael的答案:_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; 
+0

或'my $ _;'在地图之前会导致地图使用(5.10+) – ysth 2010-08-22 18:23:56

+0

@ysth“my $ _”将创建一个新的变量,以语法形式映射到地图块,但不会影响_some_func()和_do_stuff_to_file()上下文,所以这不会影响解决问题。 – dolmen 2010-08-23 10:04:01

+0

是的,它将解决* that * map的问题,除非您希望*他们能够更改@array中的别名值。 – ysth 2010-08-23 13:46:18

4

设置$ _的大部分内容都会隐式别名,所以不会导致此问题;例外是while (<filehandle>)。虽然您可以本地化$ _(理想情况下为my $_;),但最好不要隐式设置$ _。改为while (my $line = <filehandle>)。 (特殊的隐式定义()仍然会发生。)