5

我看到了这个问题:“标准”和块包声明之间有什么区别吗?和思考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 ----

问题一:上面说的对吗?主包会发生这种情况吗?

现在是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 语法的上下文中如何处理pragmas,BEGIN/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????
  }

对不起,文字墙...

4

2 回答 2

4

当您用 声明变量时my,它不在任何包中。完全没有。块作用域与任何包都严格不同。该变量}在最里面的封闭块的右大括号 ( ) 之前有效,仅没有包限定。如果你写$main::varor $::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包时,它没有任何作用,如果它与块相关联,它的行为就像任何块。

于 2013-08-01T09:38:08.003 回答
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

在这里我们可以看到 BEGIN 块是在if (0)优化之前执行的,因为 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

所以关于这个:

问题是 - 在这里使用类似的东西有什么好处

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

package main {
  my $var;
}

答案是否定的:如果我们已经在 packagemain中,重新声明它没有任何好处:

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

如果我们执行它,我们可以看到我们一直在main

Pragma 喜欢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

于 2013-08-01T09:48:26.173 回答