在 foreach 中调用的 sub 中使用来自外部作用域的变量

Using a variable from outer scope in a sub called in foreach

我正在使用 cygwin。 这个脚本的作用是加载我已经加载到桌面目录中的 iphone 图片。 它在图像查看器中打开它,让我看一下图片。

system("cygstart $dirname/$oldfile") ;

然后它让我可以选择重命名图片。虽然它会抛出错误,但不会重命名图片。

Use of uninitialized value $oldfile in concatenation (.) or string at ./rename_image.pl line 29, <STDIN> line 6.

oldfile 是一个全局变量,函数应该看到这个变量。

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

my $oldfile;
my $new_name;
my $dirname = "/cygdrive/c/Users/walt/Desktop/iphonepics/bunk_box/";
opendir(DIR, $dirname) or die "Cannot open dir: $!";

my @files = readdir(DIR);
foreach $oldfile (@files) {
        system("cygstart $dirname/$oldfile") ;
        print "Do you want to rename $oldfile ? ";
        my $input = <STDIN> ;
        chomp $input ;
        if($input =~ m/^[y]$/i) {
                rename_file() ;
        } else {
        my $doo = 1 ;
        }
}

sub rename_file {
        use File::Copy qw(move) ;
        print "New name:\n" ;
        my $new_name = <STDIN> ;
        chomp $new_name ;
        move "$dirname/$oldfile", "$dirname/$new_name";
        return ;
}

foreach $oldfile (@files) 没有给顶部声明的 my $oldfile 赋值。那是一个词法变量,而不是全局变量,在这种情况下,会为循环范围创建一个新的词法 $oldfile 。这是 foreach 中的一个特别的 属性。有关详细信息,请参阅

由于 foreach 中的 $oldfile 动态范围 中的词法,因此它对 sub 是不可见的。但是 sub 看到 my $oldfile 为文件的静态范围声明。来自 Private Variables via my()

... lexical variables declared with my are totally hidden from the outside world, including any called subroutines. This is true if it's the same subroutine called from itself or elsewhere--every call gets its own copy.

This doesn't mean that a my variable declared in a statically enclosing lexical scope would be invisible. Only dynamic scopes are cut off. For example, ...

顶部的 $oldfile 被 sub 看到,保持循环之前的状态,未初始化。

总而言之,获得预期行为的简单 "fix" 是使 $oldfile 成为全局变量,因此将 my $oldfile 替换为 our $oldfile

但是,为什么要依赖全局变量呢?为什么不将他们需要的传递给函数

foreach my $oldfile (@files) {
    ...
    if (...) {
        rename_file($dirname, $oldfile);
    ...
}

sub rename_file {
    my ($dirname, $oldfile) = @_;
    ...
}

已经很好地使用 strict 并声明了其他所有内容。那么你不需要也不应该在顶部声明文件范围的词法my $oldfile;

当您在子程序中声明一个变量时,它不会受到子程序范围外同名变量的影响。所以在这里你 知道 $oldfile 是什么——它正是你传递给函数的内容。

这样你的 sub 也有一个定义良好的接口并且不依赖于周围代码中的任意值。这对于明确划分范围和模块化至关重要。

更一般地说,在 strict 到位并声明其他内容时使用全局变量就像为维护程序员埋下地​​雷,或者为六个月后的你自己埋下地雷。

我建议也阅读 Mark-Jason Dominus 的 Coping with Scoping


我不推荐这个,除非有一个非常具体和可靠的理由需要一个类似全局的 our 变量。