Perl的主要包 - 块语法 - pragma和BEGIN / END块

时间:2013-08-01 08:19:35

标签: perl

我看到了这个问题:Is there any difference between "standard" and block package declaration?并在考虑main package。当我编写脚本时,例如:

---- begin of the file ---
#!/usr/bin/perl  #probably removed by shell?
my $var; #defined from now up to the end of file
...
---- end of the file ----

这会自动进入main包,所以我明白了,接下来会发生。

---- begin of the file ---
{ #<-- 1st line
  package main;
  my $var; #variable transformed to block scope - "up to the end of block"
  ...
} # <-- last line
---- end of the file ----

相当于

---- begin of the file ---
package main { #1st line
  my $var; #variable block scope
  ...
} #last line
---- end of the file ----
问题1:以上是对的吗?主包发生了这种情况吗?

现在BEGIN/END阻止和编译指示。如果我理解的话,在编译阶段处理。所以:

---- begin of the file ---
#!/usr/bin/perl
use strict;    #file scope
use warnings;  #file scope
my $var; #defined from now up to the end of file
BEGIN {
    say $var; #the $var is not known here - but it is declared
}
...
---- end of the file ----

声明$var,但这里

---- begin of the file ---
#!/usr/bin/perl
use strict;    #file scope
use warnings;  #file scope

BEGIN {
    say $var; #the $var is not known here - but "requires explicit package name" error
}

my $var; #defined from now up to the end of file
...
---- end of the file ----

未声明$var

那么如何将上述内容翻译成“默认主程序包”?

总是:

---- begin of the file ---
{
  package main;
  use strict;    #block scope ???
  use warnings;  #block scope ???
  my $var; #defined from now up to the end of block

  BEGIN { #NESTED???
    say $var; #the $var is not known here - but declared
  }
  ...
}
---- end of the file ----

相当于

---- begin of the file ---
package main {
  use strict;    #block scope
  use warnings;  #block scope
  my $var; #defined from now up to the end of block

  BEGIN {  #NESTED block
    say $var;
  }
  ...
}
---- end of the file ----

问题是 - 在这里 _ANY 使用类似的东西:

  ---- begin of the file ---
  use strict;   #always should be at the START OF THE FILE - NOT IN BLOCKS?
  use warnings;

  #not NESTED
  BEGIN {
  }

  package main {
     my $var;
  }

所以问题是:

  • BLOCK语法的上下文中,pragmasBEGIN/END/CHECK块和main package的确切处理方式是什么?
  • 将“文件范围”更改为“块范围”时 - 或者如果它没有更改,“标准主程序包”与“主程序包{block}”的等效翻译是什么

和最后一段代码:

  ---- begin of the file ---
  use strict;   #always should be at the START OF THE FILE - NOT IN BLOCKS?
  use warnings;
  my $var;

  #not NESTED
  BEGIN {
  }

  package main {

  }

my $var如何进入main package?所以这被转化为:

  ---- begin of the file ---
  use strict;   #always should be at the START OF THE FILE - NOT IN BLOCKS?
  use warnings;

  #not NESTED
  BEGIN {
  }

  package main {
      my $var; #### GETS HERE????
  }

对不起文字之墙......

2 个答案:

答案 0 :(得分:4)

当您使用my声明变量时,不在任何包中。完全没有。块范围与任何包都严格不同。该变量在最内层封闭块的右括号(})之前有效,且没有包限定。如果您撰写了$main::var$::var,那么它将是不同的变量。

use warnings;
use strict;
package main {
    my $var = 'this';
}
$var; # error, $var was not declared in this scope
say $main::var; # says nothing

还有两种方法可以声明变量:

  • use vars qw($var)使$var引用包中的当前包中的变量。
  • our $var使$var引用当前块中our语句时当前包中的变量。

块包声明是一个块,并将其内容放在包中。而无块包声明将以下内容放在另一个包中,但当前块范围仍在继续。

另一个缺失的是你写的时候

 use warnings;
 use strict;
 package main {
 # ...
 }

你已经有效地写了

 package main {
     use warnings;
     use strict;
     package main {
     # ...
     }
 }

并且因为包是相同的,所以与

相同
 package main {
     use warnings;
     use strict;
     {
     # ...
     }
 }

换句话说,包在文件开头是main,并且隐式块作用域(文件范围)是打开的。当您重新输入main包时,它没有任何效果,如果它与块关联,它的行为与任何块一样。

答案 1 :(得分:1)

范围和执行顺序彼此无关。

是的,默认包是main。所以可以

---- begin file ----
1: #!/usr/bin/perl
2: my $var;
3: ...;
---- end file ----

相当于

package main {
---- begin file ----
1: #!/usr/bin/perl
2: my $var;
3: ...;
---- end file ----
}

只是假定main包,除非指定了另一个包。这不会改变行号等。

遇到变量声明时,会立即将其添加到已知变量列表中。或者更准确地说,只要声明它的声明已经结束:

my      # $var unknown
$var    # $var unknown
=       # $var unknown
foo()   # $var unknown
;       # NOW $var is declared

类似于编译指示:完全解析时会立即执行use语句。在下一个声明中,所有导入都可用。

BEGIN之类的块在正常控制流之外执行,但遵守范围规则。

BEGIN块在完全解析后立即执行。返回值被丢弃。

当解释器以正常方式退出时,将执行END块。

当我们有

my $var = 1; # $var is now declared, but the assignment is run-time
BEGIN {
 # here $var is declared, but was not assigned yet.
 $var = 42; # but we can assign something if we like
}
# This is executed run-time: $var == 1
say $var;
BEGIN {
  # This is executed immediately. The runtime assignment has not yet happened.
  # The previous asignment in BEGIN did happen.
  say $var;
}

结果?

42
1

请注意,如果我在运行时没有分配新值,则此变量将保留其编译时间值:

my $var;
...; # rest as before

然后我们得到

42
42

块可以任意嵌套:

my $var;
if (0) {
  BEGIN {
    say "BEGIN 1: ", ++$var;
    BEGIN {
      say "BEGIN 2: ", ++$var;
      BEGIN { $var = 0 }
    }
  }
}

输出:

BEGIN 2: 1
BEGIN 1: 2

在这里,我们可以看到在优化if (0)之前执行BEGIN块,因为BEGIN立即执行

我们也可以询问一个块在哪个包中:

BEGIN { say "BEGIN: ", __PACKAGE__ }
say "before package main: ", __PACKAGE__;

# useless redeclaration, we are already in main
package main {
  say "in package main: ", __PACKAGE__;
}

输出:

BEGIN: main
before package main: main
in package main: main

所以我们在重新宣布它之前就在main。包不是密封的,不可变的实体。它是我们可以随意重新输入的名称空间:

package Foo;
say "We are staring in ", __PACKAGE__;
for (1 .. 6) {
  package Bar;
  say "Loop $_ in ", __PACKAGE__;
  if ($_ % 2) {
    package Baz;
    say "... and in ", __PACKAGE__;
    BEGIN { say "just compiled something in ", __PACKAGE__ }
  } else {
    package Foo;
    say "... again in ", __PACKAGE__;
    BEGIN { say "just compiled something in ", __PACKAGE__ }
  }
}

输出:

just compiled something in Baz
just compiled something in Foo
We are staring in Foo
Loop 1 in Bar
... and in Baz
Loop 2 in Bar
... again in Foo
Loop 3 in Bar
... and in Baz
Loop 4 in Bar
... again in Foo
Loop 5 in Bar
... and in Baz
Loop 6 in Bar
... again in Foo

关于这一点:

  

问题是 - 在这里 ANY 使用类似的东西

---- begin of the file ---
use strict;
use warnings;

package main {
  my $var;
}

答案是:如果我们已经在main包中,则重新声明它没有任何好处:

say __PACKAGE__;
package main {
  my $var;
  say __PACKAGE__;
}
say __PACKAGE__;

如果我们执行该操作,我们可以看到我们一直在main

strictwarnings这样的语用库具有词法范围,所以尽早声明它们是好的。

# no strict yet
use strict;
# strict now activated

BEGIN {
  # we are still in scope of strict
  $var = 1; # ooh, an undeclared variable. Will it blow up?
  say "BEGIN was executed";
}

my $var;

输出:

Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.

该变量未在BEGIN块中声明,因为它在声明之前被编译并且(未完全执行)。因此,strict发出此错误。由于在编译BEGIN块期间发生此错误,因此未执行此块。

由于采用作用域,您无法始终以避免使用BEGIN块的方式重新排序源代码。这是你永远不应该做的事情:

for (1 .. 3) {
  my $var;
  BEGIN { $var = 42 };
  say $var // "undef";
}

输出:

42
undef
undef

因为只要剩下块就清空$var。 (这可能是未定义的行为,可能会发生变化。至少在v5.16.3和v5.14.2下运行。)

编译程序时,不会进行重新排序。相反,BEGIN块在编译后立即执行。

对于运行CHECK和END的确切时间,请通读perlmod