为什么调用这个函数会改变我的数组?

时间:2022-12-01 23:35:08

Perl seems to be killing my array whenever I read a file:

每当我读取文件时,Perl似乎都在杀死我的数组:

my @files = ("foo", "bar", "baz");
print "Files: " . join(" ", @files) . "\n";

foreach(@files) {
   print "The file is $_\n";
   func();
}

sub func {
   open(READ, "< test.txt");
   while(<READ>) {
   }
   close READ;
}

print "Files: " . join(" ", @files) . "\n";

produces:

Files: foo bar baz
The file is foo
The file is bar
The file is baz
Files:

but when I comment out func(), it gives what I would've expected:

但是当我注释掉func()时,它给出了我所期望的:

Files: foo bar baz
The file is foo
The file is bar
The file is baz
Files: foo bar baz

Any ideas why this might be happening?

任何想法为什么会这样?

2 个解决方案

#1


12  

You have to change foo to localize $_, or not to use $_ in your loop. Best yet, do both:

你必须改变foo来本地化$ _,或者不要在你的循环中使用$ _。最好的,两个都做:

foreach my $filename (@files) {
    print "The file is $filename\n";
    func();
}

sub func {
    local $_;
    open my $read, '<', 'test.txt' or die "Couldn't open test.txt: $!";
    while(<$read>) {
    }
    close $read or die "Couldn't close file: $!";
}

The foreach loop aliases $_ to the current name of the file and the while(<READ>) assigns to $_. That's a bad combination of magic, so to say.

foreach循环将$ _替换为文件的当前名称,while( )分配给$ _。这就是魔法的糟糕组合,可以这么说。

In general, it's a bad idea to rely on much on $_ for anything other than a one-liner.

一般来说,除了单线之外,依靠$ _来做任何其他事情都是一个坏主意。

#2


3  

This should have been a comment to Leon's answer, but I do not have the reputation yet; my apologies.

这应该是对莱昂答案的评论,但我还没有声誉;我很抱歉。

Minor bug: $filename should have replaced $_ in the body of the foreach loop, too.

小错误:$ filename也应该替换foreach循环体中的$ _。

Minor nitpick: I would advise to always use the three argument form of open using a lexical filehandle, even in short examples.

轻微的挑剔:我建议使用lexical文件句柄始终使用open的三个参数形式,即使在简短的例子中也是如此。

foreach my $filename (@files) {
    print "The file is $filename\n";
    func();
}

sub func {
   open my $read, '<', 'test.txt' or die $!;
   while(<$read>) {
   }
   close $read or die $!;
}

print 'Files: ' . join(' ', @files) . "\n";

#1


12  

You have to change foo to localize $_, or not to use $_ in your loop. Best yet, do both:

你必须改变foo来本地化$ _,或者不要在你的循环中使用$ _。最好的,两个都做:

foreach my $filename (@files) {
    print "The file is $filename\n";
    func();
}

sub func {
    local $_;
    open my $read, '<', 'test.txt' or die "Couldn't open test.txt: $!";
    while(<$read>) {
    }
    close $read or die "Couldn't close file: $!";
}

The foreach loop aliases $_ to the current name of the file and the while(<READ>) assigns to $_. That's a bad combination of magic, so to say.

foreach循环将$ _替换为文件的当前名称,while( )分配给$ _。这就是魔法的糟糕组合,可以这么说。

In general, it's a bad idea to rely on much on $_ for anything other than a one-liner.

一般来说,除了单线之外,依靠$ _来做任何其他事情都是一个坏主意。

#2


3  

This should have been a comment to Leon's answer, but I do not have the reputation yet; my apologies.

这应该是对莱昂答案的评论,但我还没有声誉;我很抱歉。

Minor bug: $filename should have replaced $_ in the body of the foreach loop, too.

小错误:$ filename也应该替换foreach循环体中的$ _。

Minor nitpick: I would advise to always use the three argument form of open using a lexical filehandle, even in short examples.

轻微的挑剔:我建议使用lexical文件句柄始终使用open的三个参数形式,即使在简短的例子中也是如此。

foreach my $filename (@files) {
    print "The file is $filename\n";
    func();
}

sub func {
   open my $read, '<', 'test.txt' or die $!;
   while(<$read>) {
   }
   close $read or die $!;
}

print 'Files: ' . join(' ', @files) . "\n";