【问题标题】:Perl smartmatch operator or grep to check if value exists in arrayPerl smartmatch 运算符或 grep 检查数组中是否存在值
【发布时间】:2013-12-10 22:20:54
【问题描述】:

在阅读了最近关于检查数组中是否存在值的 Perl 问题后,我开始思考如何做到这一点。我看到大多数人在表单中推荐 grep 选项

if (!grep { $input_day eq $_ } @days ) {
    say "Grep Invalid Day";
}

但是当我读到这个问题时,我第一次跳到了智能匹配运算符

unless ( $input_day ~~ @days ) {
    say "Smart Invalid Day";
}

所以我想知道使用 grep 是否比智能匹配有任何好处,反之亦然。我知道智能匹配仅在更高版本的 Perl 中可用,因此不建议使用 5.10.1 之前的 Perl 版本的人。

我之前从未真正对 Perl 代码进行过基准测试,因此以下代码是根据在线示例编写的。我已经尝试运行 smart match 示例 200 万次和 grep 示例 200 万次并记录时间。

use strict;
use warnings;
use v5.16.2;
use Benchmark;

my $input_day = shift;
my @days = qw /mon tue wed thu fri sat sun/;

my $smart_test_start = new Benchmark();
for(my $x=0; $x<10000000; $x++){
        unless ( $input_day ~~ @days ) {
                #here we would execute some code
        }
}
my $smart_test_end = new Benchmark();

my $grep_test_start = new Benchmark();
for(my $y=0; $y<10000000; $y++){
        if (!grep { $input_day eq $_ } @days ) {
                #here we would execute some code
        }
}
my $grep_test_end = new Benchmark();

my $smart_diff = timediff($smart_test_end, $smart_test_start);
my $grep_diff = timediff($grep_test_end, $grep_test_start);

say "SMART: ", timestr($smart_diff,'all');
say "GREP: ", timestr($grep_diff,'all');

我使用了几种不同的输入。

输入“mon”

SMART:  3 wallclock secs ( 2.75 usr  0.00 sys +  0.00 cusr  0.00 csys =  2.75 CPU)
GREP: 12 wallclock secs (12.02 usr  0.01 sys +  0.00 cusr  0.00 csys = 12.03 CPU)

输入“周四”

SMART:  6 wallclock secs ( 5.67 usr  0.00 sys +  0.00 cusr  0.00 csys =  5.67 CPU)
GREP: 11 wallclock secs (11.46 usr  0.01 sys +  0.00 cusr  0.00 csys = 11.47 CPU)

输入“太阳”

SMART:  8 wallclock secs ( 8.87 usr  0.01 sys +  0.00 cusr  0.00 csys =  8.88 CPU)
GREP: 12 wallclock secs (11.62 usr  0.00 sys +  0.00 cusr  0.00 csys = 11.62 CPU)

输入“非”

SMART:  9 wallclock secs ( 8.46 usr  0.00 sys +  0.00 cusr  0.00 csys =  8.46 CPU)
GREP: 11 wallclock secs (11.58 usr  0.13 sys +  0.00 cusr  0.00 csys = 11.71 CPU)

在所有情况下,智能匹配运算符似乎都比 grep 执行得更好。查看结果,我假设在早期用例中,这是因为智能匹配将在找到匹配项后立即停止,而 grep 将在匹配第一次匹配后继续检查数组的其余部分。

然后我看到其他人建议使用某些模块来查找第一个实例等。

人们不推荐智能匹配运算符有什么原因吗?智能匹配是否存在一些限制或不可靠性?

【问题讨论】:

标签: perl


【解决方案1】:

不要重复不要在生产代码中使用 smartmatch 运算符。根据perldelta smartmatch 已被标记为实验性:

智能匹配,在 v5.10.0 中添加并在 v5.10.1 中进行了重大修改,一直是投诉点。尽管它有很多有用的方式,但它也被证明对于 Perl 的用户和实现者来说是有问题的和令人困惑的。关于如何最好地解决这个问题已经提出了许多建议。很明显,smartmatch 几乎肯定会在未来改变或消失。不建议依赖其当前行为。

现在,当解析器看到 ~~、given 或 when 时会发出警告。要禁用这些警告,您可以将此行添加到适当的范围:

no if $] >= 5.018, "experimental::smartmatch";

不过,请考虑更换这些功能的使用,因为它们可能会在变得稳定之前再次改变行为。

这意味着在这些问题得到解决之前,依赖此功能的代码不能被认为是稳定的。

【讨论】:

  • This 是您想要的部分。
  • @ysth 链接是正确的,只是涂错了。我猜是在写这篇文章的时候分心了。
  • "依赖于这个特性的代码不能被认为是稳定的" 如果你希望最终升级到 perl 5.22 或更高版本。 (如果基本的 $string ~~ @array_of_strings 案例受到任何没有完全删除 ~~ 的更改的影响,我会感到惊讶,如果它最终被删除,如果它发生在 5.24 之前,我会感到非常惊讶。)
【解决方案2】:

解决这个问题的正确方法是使用哈希而不是数组

my %days = map { $_ => 1 } @days

那你就可以写了

unless ($days{$input_day}) {
  say "Hash Invalid Day";
}

并且性能将远远超过任何其他解决方案。

(我希望这很明显,但您应该只设置一次哈希,然后在所有测试中继续使用它。)

【讨论】:

  • @woolstar:另外,应该是@days{@days} = ()
  • 它最终会为大型数据集使用更少的内存,因为它不必为每个条目分配“值”。 undef 是一个没有存储开销的内在魔法。
  • @woolstar:而且你的内存太少了,连七个标量变量都买不起,还值得把代码模糊一下?
  • 七?我们的用例通常使用 10MM 到 100MM 的密钥。也是这家店里大家都熟悉的成语。
  • @woolstar:OP的问题有一个七元素数组。其他任何事情在很大程度上都无关紧要。你的意思是10到1亿? (mm 是毫米。)如果是这样,那么该大小的数据属于数据库。
【解决方案3】:

想补充一下我在这方面的经验,因为我做了一些测试。我一直使用 Smart Match,最近厌倦了它会产生的警告。

我有一个1亿的文本文件,10个字符串。

Perl 脚本将 STDIN 转换为一个数组,并执行 3 种常用方法来查找数组中是否存在字符串。我尝试使用上面建议的哈希映射,但是与数组相比,哈希映射需要 3 倍的时间来生成。如果您正在对现有值进行广泛的测试,这种权衡在某些时候可能是可以的,因为对哈希的存在检查基本上是即时的。这也取决于您的数据源。

未来我计划主要使用 List::Util (any),因为它的未来证明是一个核心模块并且性能可靠。

#!/usr/bin/perl
use List::Util qw(any);
my @arr = qw(a b c d e);
if ( any { $_ eq 'd' } @arr ) { 
    print "Found.\n";
}

方法:

List::Util (any): if ( any { $_ eq $a } @arr ) { do something. }
Perl Smartmatch: if ( $b ~~ @arr ) { do something. }
Grep: if ( grep { $c eq $_ } @arr ) { do something. }

我搜索了我知道存在于 1,10,100,1000,10000,100000,1000000,10000000,100000000,100000000,100000000 个位置中的值。计时是通过 Time::HiRes 模块完成的。

我发现,如果您的大多数值都在数组的开头,则 smartmatch 将执行 List::Utils 方法。但是,如果您的大多数值位于中间或末尾或数组 List::Util 中不存在,则将无法执行。无论是否找到值,grep 似乎都会进行详尽的搜索。

更多输出细节:

Smart Match total: 5.939 seconds.
List::Util::any: 7.332 seconds.
Grep total: 39.553 seconds.

Array Generation Time: 30.315 seconds. Searching 100000000 arr elements.

any Searching eavTa2eWr1 any Found - eavTa2eWr1. Time: 0.540 seconds. any Searching mhEusMj5E7 any Found - mhEusMj5E7. Time: 0.358 seconds. any Searching WGwHfJICK6 any Found - WGwHfJICK6. Time: 0.364 seconds. any Searching I48fNDYNKF any Found - I48fNDYNKF. Time: 0.359 seconds. any Searching q3YVBTmX9J any Found - q3YVBTmX9J. Time: 0.357 seconds. any Searching pw0J5vRCnW any Found - pw0J5vRCnW. Time: 0.358 seconds. any Searching GNJP5flX5z any Found - GNJP5flX5z. Time: 0.392 seconds. any Searching 3Mh0x0R3OC any Found - 3Mh0x0R3OC. Time: 0.649 seconds. any Searching H5yxSA7eDx any Found - H5yxSA7eDx. Time: 3.473 seconds. List::Util::any: 6.850 seconds.

###############################################################

SM Searching eavTa2eWr1 SM Found eavTa2eWr1. Time: 0.000 seconds. SM Searching mhEusMj5E7 SM Found mhEusMj5E7. Time: 0.000 seconds. SM Searching WGwHfJICK6 SM Found WGwHfJICK6. Time: 0.000 seconds. SM Searching I48fNDYNKF SM Found I48fNDYNKF. Time: 0.000 seconds. SM Searching q3YVBTmX9J SM Found q3YVBTmX9J. Time: 0.001 seconds. SM Searching pw0J5vRCnW SM Found pw0J5vRCnW. Time: 0.005 seconds. SM Searching GNJP5flX5z SM Found GNJP5flX5z. Time: 0.054 seconds. SM Searching 3Mh0x0R3OC SM Found 3Mh0x0R3OC. Time: 0.519 seconds. SM Searching H5yxSA7eDx SM Found H5yxSA7eDx. Time: 5.083 seconds. Smart Match total: 5.662 seconds.

############################################################### Grep Searching eavTa2eWr1 Grep Found eavTa2eWr1. Time: 4.648 seconds. Grep Searching mhEusMj5E7 Grep Found mhEusMj5E7. Time: 4.546 seconds. Grep Searching WGwHfJICK6 Grep Found WGwHfJICK6. Time: 4.295 seconds. Grep Searching I48fNDYNKF Grep Found I48fNDYNKF. Time: 4.262 seconds. Grep Searching q3YVBTmX9J Grep Found q3YVBTmX9J. Time: 4.282 seconds. Grep Searching pw0J5vRCnW Grep Found pw0J5vRCnW. Time: 4.462 seconds. Grep Searching GNJP5flX5z Grep Found GNJP5flX5z. Time: 4.420 seconds. Grep Searching 3Mh0x0R3OC Grep Found 3Mh0x0R3OC. Time: 4.185 seconds. Grep Searching H5yxSA7eDx Grep Found H5yxSA7eDx. Time: 4.112 seconds. Grep total: 39.214 seconds. Done.

检查不存在的值。

List::Util::any: 28.980 seconds.
Grep total: 34.790 seconds.
Smart Match total: 42.913 seconds.

Array Generation Time: 30.909 seconds. Searching 100000000 arr elements.

any Searching eavTa2eWr1l Time: 3.264 seconds. any Searching mhEusMj5E7l Time: 3.404 seconds. any Searching WGwHfJICK6l Time: 3.291 seconds. any Searching I48fNDYNKFl Time: 3.240 seconds. any Searching q3YVBTmX9Jl Time: 3.083 seconds. any Searching pw0J5vRCnWl Time: 3.247 seconds. any Searching GNJP5flX5zl Time: 3.180 seconds. any Searching 3Mh0x0R3OCl Time: 3.028 seconds. any Searching H5yxSA7eDxl Time: 3.243 seconds. List::Util::any: 28.980 seconds.

###############################################################

SM Searching eavTa2eWr1l Time: 4.620 seconds. SM Searching mhEusMj5E7l Time: 4.783 seconds. SM Searching WGwHfJICK6l Time: 4.899 seconds. SM Searching I48fNDYNKFl Time: 4.902 seconds. SM Searching q3YVBTmX9Jl Time: 4.863 seconds. SM Searching pw0J5vRCnWl Time: 4.646 seconds. SM Searching GNJP5flX5zl Time: 4.751 seconds. SM Searching 3Mh0x0R3OCl Time: 4.666 seconds. SM Searching H5yxSA7eDxl Time: 4.782 seconds. Smart Match total: 42.913 seconds.

###############################################################

Grep Searching eavTa2eWr1l Time: 4.034 seconds. Grep Searching mhEusMj5E7l Time: 3.849 seconds. Grep Searching WGwHfJICK6l Time: 3.837 seconds. Grep Searching I48fNDYNKFl Time: 3.822 seconds. Grep Searching q3YVBTmX9Jl Time: 3.923 seconds. Grep Searching pw0J5vRCnWl Time: 3.825 seconds. Grep Searching GNJP5flX5zl Time: 3.994 seconds. Grep Searching 3Mh0x0R3OCl Time: 3.846 seconds. Grep Searching H5yxSA7eDxl Time: 4.174 seconds. Grep total: 35.303 seconds. Done.

【讨论】:

    猜你喜欢
    • 2020-01-05
    • 1970-01-01
    • 2020-01-05
    • 2016-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多