Perl:使用Module @list

时间:2013-12-05 09:55:15

标签: perl perl-module

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

my @list = qw(foo zoo);
use Module @list;

而不是

use Module qw(foo zoo);

所以她写道:

my @consts = qw(PF_INET PF_INET6);
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;

看似按预期工作:

  

2,10

然后她正在使用其他模块,例如Time::HiRes。而不是

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

0,1

她做了:

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

0,0

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

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

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

my @consts = ();
use Socket @consts;
printf "%d, %d\n", PF_INET, PF_INET6;
  

2,10

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

use Socket ();
printf "%d, %d\n", PF_INET, PF_INET6;
  

0,0

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

她的结论是use Module @list不能像她期望的那样发挥作用。

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

1 个答案:

答案 0 :(得分:19)

这与代码执行时有关。 use在编译时执行,而my @list仅在运行时执行。因此,数组不存在于模块加载的位置。

模块Socket exports PF_INETPF_INET6默认情况下,如果您将其放在use行中并不重要。但默认情况下,Time :: HiRes does not export stuff

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;

What use doesrequire模块,import是编译时参数的LIST。所以它与:

相同
BEGIN {
  require foo;
  foo->import();
}

知道这一点,我们可以自己做:

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

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

由于范围界定,只需在使用前添加BEGIN块,即可。

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

use Time::HiRes (@consts);

您可以通过在BEGIN块之外声明变量来解决问题。这样,它将在下一个BEGIN块的范围内可用,并且因为BEGIN blocks are executed at compile time in FIFO order已经设置了值。

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

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

__END__
0, 1

所以回顾一下:

  • 这是因为两件事:范围执行顺序
  • 您传递数组的事实不是问题 - 数组将是传递的part of the LIST
  • 如果您不use strict,则无法轻易找到问题
  • 如果您在BEGIN前面添加use块并将my声明置于BEGIN之外,则可以
  • 如果您自己采用require代替useimport,您也可以传递数组