【问题标题】:Why subroutine needs to be written after the declaration of variables used in it?为什么子程序需要在声明变量之后再写呢?
【发布时间】:2016-10-06 13:10:08
【问题描述】:

假设我们有这段代码,为什么它会因显式包名错误而失败,因为该函数仅在声明 $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";
}

忽略这种编程风格的无用或已弃用的编码风格,只关注上下文。

【问题讨论】:

  • 我编辑了问题类型,现在可能更清晰了一些
  • 即使没有strict,我仍然不会“工作”——子程序仍将使用包$value,而不是稍后声明的词法包。

标签: perl scope subroutine


【解决方案1】:

因为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 将在范围内由最新的“包”声明的命名空间(默认为main)查找“超级全局”变量除外。这指的是 Perl 在 main 而不是当前包中查找的符号变量(如 $_$$ 等)。

由于$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

【讨论】:

  • 当您说It will look at: ... the entire file scope 时,entire file 是指在子程序声明之前声明的所有内容?
  • @John 是也不是。在编译时,它只会知道在它所在的那一行之前已经声明的变量。这与子例程声明不同。在运行时,该值也可以在代码中进一步设置,但是当您在子程序中访问它时,它仍然会访问它。
  • 感谢您分享所有这些知识!
  • 当他们说“它将查看整个文件范围”时,他们说错了。范围是变量可见的地方。结果,你的问题没有意义。 /// 他们还将our 列为包变量,但它实际上是词法的,因此在列表中排序错误。 /// 我目前正在解决这些问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-21
  • 1970-01-01
相关资源
最近更新 更多