Perl';s主包-块语法-杂注和开始/结束块

Perl';s主包-块语法-杂注和开始/结束块,perl,Perl,我看到了这个问题:并且思考了主包。当我写脚本时,比如: ---- 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 l

我看到了这个问题:并且思考了
主包
。当我写脚本时,比如:

---- 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 ----
问题1:以上说法正确吗?主程序包会发生这种情况吗

现在
BEGIN/END
块和pragmas。如果我没有弄错的话,在编译阶段会有一些问题需要处理。因此:

---- 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 ----
问题是-这里有什么好处吗

  ---- 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;
  }
---- begin of the file ---
use strict;
use warnings;

package main {
  my $var;
}
因此,问题是:

  • 块语法的上下文中,如何准确处理
    杂注
    开始/结束/检查
    块和
    主包
  • 当将“文件范围”更改为“块范围”时,或者如果没有更改,“标准主包”到“主包{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
是如何进入
主程序包的?
因此这在某种程度上被翻译为:

  ---- 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????
  }

很抱歉出现了一堆文本…

当您使用
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
还有两种方法可以声明变量:

  • 使用变量qw($var)
    使
    $var
    在包内的任何位置引用当前包中的变量
  • 我们的$var
    使
    $var
    引用当前块中的
    我们的
    语句时在包中的当前变量
块包声明是一个块,将其内容放入包中。而无块包声明将以下内容放在另一个包中,但当前块范围继续

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

 use warnings;
 use strict;
 package main {
 # ...
 }
你写得很好

 package main {
     use warnings;
     use strict;
     package main {
     # ...
     }
 }
既然包装是一样的,那就和

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

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

范围和执行顺序彼此几乎没有关系

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

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

---- begin of the file ---
package main { #1st line
  my $var; #variable block scope
  ...
} #last line
---- end of the 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
与pragmas类似:完全解析at后,立即执行
use
语句。在下一个语句中,所有导入都可用

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

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

当解释器以正常方式退出时,执行结束块

当我们有

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: main
before package main: main
in package main: main
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
Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.
42
undef
undef
这里我们可以看到,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 2: 1
BEGIN 1: 2
BEGIN: main
before package main: main
in package main: main
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
Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.
42
undef
undef
因此,在重新声明之前,我们处于
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__ }
  }
}
输出:

BEGIN 2: 1
BEGIN 1: 2
BEGIN: main
before package main: main
in package main: main
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
Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.
42
undef
undef
关于这一点:

问题是-这里有什么好处吗

  ---- 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;
  }
---- begin of the file ---
use strict;
use warnings;

package main {
  my $var;
}
答案是否定的:如果我们已经在包
main
中,重新说明它没有好处:

say __PACKAGE__;
package main {
  my $var;
  say __PACKAGE__;
}
say __PACKAGE__;
如果我们执行该命令,我们可以看到我们一直处于
main
状态

strict
warnings
这样的杂注有词法范围,所以尽早声明它们是好的

# 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;
输出:

BEGIN 2: 1
BEGIN 1: 2
BEGIN: main
before package main: main
in package main: main
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
Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.
42
undef
undef
变量没有在BEGIN块内声明,因为它是在声明之前编译和执行的(没有完全执行)。因此,
strict
发出此错误。由于此错误发生在编译
BEGIN
块期间,因此未执行此块

由于作用域,您不能总是以避免使用
BEGIN
块的方式对源代码重新排序。以下是你永远不应该做的事情:

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

BEGIN 2: 1
BEGIN 1: 2
BEGIN: main
before package main: main
in package main: main
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
Global symbol "$var" requires explicit package name at - line 8.
BEGIN not safe after errors--compilation aborted at - line 10.
42
undef
undef
因为每当块离开时,
$var
就被清空。(这可能是未定义的行为,并且可能会更改。这至少在v5.16.3和v5.14.2下运行)

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


有关运行“检查”和“结束”的确切时间,请通读。

您是否尝试过所有可能的方法?在变量中添加一些值并打印一些内容,然后查看。请发布可编译的代码;必须修复
使用warnigs
使用警告不会激发信心。你写过几次“what is equired to”,但我认为你的意思是“what is equired to”。使用“what”通常被视为提问。我还没有解决这个问题,因为你可能想问