如何在模块中添加排序功能?

3

如何在模块中包含自定义排序函数?

例如,如果我创建了以下脚本(test.pl):

#!/usr/bin/perl

use 5.022;
use warnings;
use diagnostics;
use utf8;
use open qw(:std :utf8);
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";

sub mysort {
    return $a cmp $b;
}

my @list = ('a', 'd', 'b', 'c');
say join "\n", sort mysort @list;

它可以正常工作。但是,当我将它们拆分成test.pl时:

#!/usr/bin/perl

use 5.022;
use warnings;
use diagnostics;
use utf8;
use open qw(:std :utf8);
binmode STDOUT, ":utf8";
binmode STDERR, ":utf8";

use my::module;

my @list = ('a', 'd', 'b', 'c');
say join "\n", sort mysort @list;

我的/module.pm文件:

package my::module;
use 5.022;
use warnings;
use diagnostics;
use utf8;
use open qw(:std :utf8);
binmode STDOUT, ':utf8';
binmode STDERR, ':utf8';

BEGIN {
    my @exp = 'mysort';
    require Exporter;
    our $VERSION   = 1.00;
    our @ISA       = 'Exporter';
    our @EXPORT = @exp;
    our @EXPORT_OK = @exp;
}

sub mysort {
    return $a cmp $b;
}

1;

我得到了以下错误信息: 在 my/module.pm 的第20行,使用未初始化的值$my::module::a进行字符串比较 (cmp) (#1) (W uninitialized) 使用未定义的值,就像它已经被定义一样。它被解释为“”或0,但可能是一个错误。 要抑制此警告,请给您的变量赋一个定义的值。

To help you figure out what was undefined, perl will try to tell you
the name of the variable (if any) that was undefined.  In some cases
it cannot do this, so it also tells you what operation you used the
undefined value in.  Note, however, that perl optimizes your program
and the operation displayed in the warning may not necessarily appear
literally in your program.  For example, "that $foo" is usually
optimized into "that " . $foo, and the warning will refer to the
concatenation (.) operator, even though there is no . in
your program.

在my/module.pm第20行,使用未初始化的变量$my::module::b进行了字符串比较(cmp),出现警告(#1)。

是否有一种方法可以将$a和$b变量导出到模块中?


尝试将“mysort”替换为“&mysort”。 - Guido Flohr
1
在发布代码之前,您可能希望删除 use diagnostics。它会增加约一秒的启动时间。 - simbabque
1
由于mysort被导出到主包中,它想要在主包中使用$a$b变量,但是mysort中的$a$b变量指的是$my::module::a$my::module::b,这些变量在该包中没有声明,并且不作为sort的特殊变量使用。请注意,您可以使用$::a$::b来引用主包中的$a$b变量,但是如果您将mysort导入到除main包之外的其他包中,则此方法将无法正常工作。@Ujin提供了更好的方法,这个方法通常都能正常工作。 - Håkon Hægland
@GuidoFlohr 我应该把 &mysort 放在哪里? - Rob
@Rob:抱歉,提示错误了。我总是使用那种语法来明确它是一个代码引用,但是将名称作为参数传递给 sort 也是可以的。 - Guido Flohr
2个回答

8

$a$b是包全局变量。在mysort中有几���访问它们的选项,但所有这些选项都有点糟糕。

你也可以使用原型变量:

sub mysort($$) {
    my ($a, $b) = @_;
    return $a cmp $b;
}

但是根据文档,这种变体速度较慢。 http://perldoc.perl.org/functions/sort.html


1
不要慢,这是最好的方法。 - ysth
是的,这个很好用:我只是在排序几千个项目,所以速度甚至不是一个因素。 - Rob
1
你可以避免一些速度惩罚,不必复制输入并直接使用@_元素。例如return $_[0] cmp $_[1];但是需要进行基准测试以了解这对于您特定的排序算法有多重要。 - Grinnz

4

如果您出于某种原因不想在 mysort 上添加 ($$) 原型(尽管我找不到想要这样做的好例子),就像 @Ujin 的回答中一样,您也可以使用 caller 来获取调用者的包名称(假设调用者始终是 sort):

sub mysort {
    my $pkg = caller;
    {
        no strict 'refs';
        return ${"${pkg}::a"} cmp ${"${pkg}::b"};
    }
}

2
一个带有 ($$) 的排序函数调用速度比一个没有它的慢,但你正在添加一个调用 caller 的语句可能会消耗这种好处。需要进行基准测试。 - ikegami

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接