Perl:使用Module @list

问题描述 投票:7回答:1

有人想在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

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

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

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

perl perl-module
1个回答
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,你也可以传递一个数组
© www.soinside.com 2019 - 2024. All rights reserved.