【问题标题】:Perl: use Module @listPerl:使用模块@list
【发布时间】:2018-09-19 10:58:11
【问题描述】:

有人想在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 语句中使用预定义数组的正确方法是什么?

【问题讨论】:

  • Re“尝试导入一个空列表......令人惊讶的效果也很好”,为什么? use foo @foo;use foo; 没有什么不同。两者都提供一个空列表。 use foo (); 也会传递一个空列表,但它是一个特殊的语法

标签: perl perl-module


【解决方案1】:

这与代码执行的时间有关。 use 在编译时执行,而my @list 仅在运行时执行。所以数组在模块加载的时候不存在。

模块Socket exportsPF_INETPF_INET6默认情况下,所以放在use那行也没关系。但是 Time::HiRes does not export stuff 默认情况下。

strict 的错误是:

在使用“strict subs”时不允许使用裸词“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 是编译时的参数列表。所以它是一样的:

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 解释器执行时已经可用。

由于作用域,仅在 use 前添加 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,就不容易发现问题
  • 如果在use 前面添加BEGIN 块并将my 声明放在BEGIN 之外,它就可以工作
  • 如果你自己使用require而不是useimport,你也可以传递一个数组

【讨论】:

  • 不必要地鼓吹no strict;。固定。
  • 你也可以use foo do { my @syms = ...; ...; @syms; };
  • 非常好。有趣的是,她仍然不开心,因为她最终想做:printf "%d, %d\n", @consts,这显然是错误的。或者,如果不导入,这也无法通过她的方式工作:print Time::HiRes::$consts[0]。她怎么能用字符串作为“符号”呢?
  • @mykhal:no strict 'refs'; printf "%d, %d\n", map &{$_}(), @consts。这将@consts 中的字符串作为子名称并执行它。仅当 strict refs 关闭时才有效。将该部分包装在一个非常短的块中是有意义的,因此它不会影响其余代码。
  • @ikegami:感谢您指出这一点并修复答案。另一方面,OP 说 它在非严格环境中,所以我认为这并不重要。 ;)
猜你喜欢
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多