Perl:使用Module @list

myk*_*hal 7 perl perl-module

有人想在use函数语句中使用数组变量代替数组(列表)文字,如:

my @list = qw(foo zoo);
use Module @list;
Run Code Online (Sandbox Code Playgroud)

代替

use Module qw(foo zoo);
Run Code Online (Sandbox Code Playgroud)

所以她写道:

my @consts = qw(PF_INET PF_INET6);
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;
Run Code Online (Sandbox Code Playgroud)

看似按预期工作:

2,10

然后,她正在使用其他模块,例如Time::HiRes.代替

use Time::HiRes qw(CLOCK_REALTIME CLOCK_MONOTONIC);
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;
Run Code Online (Sandbox Code Playgroud)

0,1

她这样做:

my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
use Time::HiRes @consts;
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;
Run Code Online (Sandbox Code Playgroud)

0,0

它突然不起作用,就像它与Socket模块一起工作!这里发生了一件坏事.

(......这是在非严格的环境中.如果她使用use strict,她甚至会出错.另一方面,她在她的第一个看似有效的例子中没有任何暗示 - 即使她在use strict; use warnings; use diagnostics那里.)

现在她想探索这种奇怪的行为.尝试导入空列表:

my @consts = ();
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;
Run Code Online (Sandbox Code Playgroud)

2,10

令人惊讶地工作,虽然它可能不应该,如:

use Socket ();
printf "%d, %d\n", PF_INET, PF_INET6;
Run Code Online (Sandbox Code Playgroud)

0,0

然后她稍微深入了解这些模块并意识到,两个模块之间的区别在于这些常量分别是未@EXPORT编辑的.

她的结论是,这use Module @list并不像她期望的那样有效.

对此最好的解释是什么?她做错了什么 - 在use声明中使用预定义数组的正确方法是什么?

sim*_*que 19

这与代码执行时有关.use在编译时执行,而my @list只在运行时执行.因此,数组不存在加载模块的点.

该模块插座出口 PF_INETPF_INET6在默认情况下,如果你把它放在不要紧use线.但是Time :: HiRes 默认情况下不会导出内容.

你得到的错误strict是:

使用"严格潜艇"时不允许使用Bareword"CLOCK_REALTIME"...

这告诉我们Perl不知道这CLOCK_REALTIME是一个sub,这是真的,因为当我们这样做时它没有被加载:

my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
use Time::HiRes @consts;
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;
Run Code Online (Sandbox Code Playgroud)

什么use确实require模块和import自变量在编译时的列表.所以它是一样的:

BEGIN {
  require foo;
  foo->import();
}
Run Code Online (Sandbox Code Playgroud)

知道了,我们可以自己做:

use strict; use warnings;
BEGIN { 
  my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
  require Time::HiRes;
  Time::HiRes->import(@consts);
}

printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

__END__
0, 1
Run Code Online (Sandbox Code Playgroud)

像这样它可以工作,因为数组@const在同一范围内定义,并且在Perl解释器执行时已经可用.

由于范围界定,只需BEGIN在使用前添加一个块将无法正常工作.

BEGIN {
  my @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
}

use Time::HiRes (@consts);
Run Code Online (Sandbox Code Playgroud)

您可以通过在BEGIN块外声明变量来解决问题.这样,它将在下一个BEGIN块的范围内可用,并且该值已经设置,因为BEGIN块在编译时以FIFO顺序执行.

my @consts;
BEGIN {
  @consts = qw(CLOCK_REALTIME CLOCK_MONOTONIC);
}

use Time::HiRes (@consts);
printf "%d, %d\n", CLOCK_REALTIME, CLOCK_MONOTONIC;

__END__
0, 1
Run Code Online (Sandbox Code Playgroud)

所以回顾一下:

  • 这是因为两件事:范围执行顺序
  • 您传递数组的事实不是问题 - 数组将成为传递的LIST的一部分
  • 如果你不这样做use strict,你就不能轻易找到问题所在
  • 如果你BEGIN在前面添加一个块use并将my声明放在BEGIN它之外,它就可以了
  • 如果你require代替useimport你自己,你也可以传递一个数组