1

与第二个示例相比,第一个带有 typeglob 的示例是否有缺点?

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

*_func_one   = \&Some::Module::_func_one;
*_func_two   = \&Some::Module::_func_two;
*_func_three = \&Some::Module::_func_three;

sub public {
    my $s = _func_one();
    for my $i ( 0 .. $s ) {
        say _func_two( $i );
    }
    say _func_three( $s );
}

1;

package Some::Module::Win32;
use strict;
use 5.10.0;

use Exporter 'import';
our @EXPORT_OK = qw(public);

use Some::Module;

sub public {
    my $s = Some::Module::_func_one();
    for my $i ( 0 .. $s ) {
        say Some::Module::_func_two( $i );
    }
    say Some::Module::_func_three( $s );
}

1;
4

1 回答 1

3

您的第一个示例显示了如何Exporter工作:通过分配类型球。但是有一个重要的区别:导入函数的时间。当子程序具有原型时,这一点最为重要。原型需要解析期间知道,因此必须在BEGIN阶段知道。use- 通常调用import使用过的包 - 在BEGIN阶段处理。

您还应该意识到,在您的第一个示例中,您的代码的用户现在可以Some::Module::Win32::_func_one(),而在第二个示例中这是不可能的(除非Some::Moduleexport _func_one.

这是在正确阶段导入函数的版本:

package Some::Module::Win32;
use strict;
use 5.10.1;  # because `parent` comes with 10.1

use parent 'Exporter';  # optimal way to inherit the `import`.
our @EXPORT_OK = qw(public);

use Some::Module;

BEGIN {
  *_func_one   = \&Some::Module::_func_one;
  *_func_two   = \&Some::Module::_func_two;
  *_func_three = \&Some::Module::_func_three;
}

sub public {
    my $s = _func_one();
    for my $i ( 0 .. $s ) {
        say _func_two( $i );
    }
    say _func_three( $s );
}

1;

如果你愿意,你可以使用一个模块namespace::autoclean,一旦你不再需要它们,就可以从符号表中删除任何外部导入。

另一种策略是将代码引用放入词法变量中。但是,语法有点难看,任何原型都被完全忽略:

package Some::Module::Win32;
use strict;
use 5.10.1;

use parent 'Exporter';
our @EXPORT_OK = qw(public);

use Some::Module;

my $func_one   = \&Some::Module::_func_one;
my $func_two   = \&Some::Module::_func_two;
my $func_three = \&Some::Module::_func_three;

sub public {
    my $s = $func_one->();
    for my $i ( 0 .. $s ) {
        say $func_two->( $i );
    }
    say $func_three->( $s );
}

1;

这不会触及符号表,因此可以认为是一个非常“干净”的解决方案。

你的第二个版本可能更冗长,但它的优点是非常明确,这可能使代码更容易被读者或维护者理解。这是我经常使用的解决方案。

  • 原型工作正常
  • 非常易读(除非函数的全名非常长,比如Marpa::R2::Inner::Scanless::G::SYMBOL_IDS_BY_EVENT_NAME_AND_TYPE
  • 明确的
  • 没有命名空间污染
于 2013-08-31T09:11:41.303 回答