为什么子例程需要在声明变量后使用呢?

时间:2016-10-06 13:10:08

标签: perl scope subroutine

假设我们有这个代码,为什么它失败并显式包名称错误,因为只有在声明$value之后才调用该函数?

use strict;
use warnings;

sub print_value{
    print "\n$value";
}

my $value = 2;
print_value();

这在编译时失败:

Global symbol "$value" requires explicit package name at file.pl line 5.
Execution of file.pl aborted due to compilation errors.

这段代码非常合适:

use strict;
use warnings;

my $value = 2;
print_value();

sub print_value{
    print "\n$value";
}

忽略这种编程风格或不推荐的编码风格的无用性,只关注上下文。

1 个答案:

答案 0 :(得分:11)

这是因为scope。这就是程序中您的变量可见的位置。您的$value词汇变量,因为您已使用my声明了该变量。这意味着,它存在于某个范围内(以及所有范围内)。在两个示例中,范围是整个文件(也可以称为全局范围)。

Perl分两个阶段查看您的代码。第一个是编译时,它检查语法并加载依赖项(如use语句)。此时,它将在函数内查找$value的可用性。

Perl会在这些地方寻找:

  1. 目前在范围内的词法(myour)变量。

    如果引用它的代码遵循声明,并且它与声明在同一个块中,或者嵌套在该块中,则变量在范围内(即变量是可见的)。文件本身就是一个块,并且形成了其他文件。

    如果范围内有多个具有相同名称的词法变量,则最近声明的变量会掩盖其他变量。这意味着函数本身声明的变量将在函数外部使用之前使用。

    my $i;          # +--------- $i is in scope (visible) here
    my $x;          # | +------- $x is in scope (visible) here
    while (...) {   # | |
        my $j;      # | |  +---- $j is in scope (visible) here
        my $x;      # |    | +-- This different $x is in scope (visible) here 
        ...         # |    v v
    }               # | |
    sub foo {       # | |
        my $j;      # | |  +---- This different $j is in scope (visible) here
        my $x;      # |    | +-- This third $x is in scope (visible) here
        ...         # |    v v
    }               # | |
    ...             # v v
    
  2. 包变量

    这些是全局变量(未声明,或使用use vars声明)。

    Perl会查看范围内最新“package”声明的命名空间(默认为main),“超全局”变量除外。这是指Perl在$_而不是当前包中查找的符号变量(如$$main等)。

  3. 由于尚未声明$value,因此Perl将其视为包变量$main::value(因为main是默认包)。

    use strict;            # | Code here will use package var $main::value
    use warnings;          # |
                           # |
    sub print_value{       # |
        print "\n$value";  # |
    }                      # |
                           # v
    my $value = 2;         # | Code here will use this lexical var $value
    print_value();         # v
    

    这一切都不是因为您strict已开启。只有必须使用myour或使用完全限定名称声明变量的事实才是use strict

    如果您没有strict并且没有使用my声明变量,那么您的程序就可以正常运行。在这种情况下,$value将是一个包变量。

    在您的第二个示例中,您已在子例程之前声明了$value,因此Perl在编译时知道该范围内将有$value可用,因此它不会抱怨。

    use strict;            # | Code here will use package var $main::value
    use warnings;          # |
                           # v
    my $value = 2;         # | Code here will use this lexical var $value
    print_value();         # |
                           # |
    sub print_value{       # |
        print "\n$value";  # |
    }                      # v
    

    但是,更好的方法是将变量作为参数传递给print_value

    use strict;
    use warnings;
    
    sub print_value{
        my $arg = shift;
        print "\n$arg";
    }
    
    my $value = 2;
    print_value($value);
    

    现在Perl看到小范围内有一个$arg。它不知道它的价值,但它没有。在使用它之前,$value也已经声明了。

    永远不要在函数内部使用全局范围(整个文件)中的变量。始终将它们作为参数传递 1

    以下是一些相关链接:

    1)除非您想构建单身或其他类型的closure