排序是否有助于提高Perl中grep的效率

排序是否有助于提高Perl中grep的效率,perl,grep,Perl,Grep,我想了解一些关于Perl的grep函数如何工作的细节。我正在这样做: if ( grep{ $foo == $_ } @bar ) { some code; } 假设@bar很大(数十万个元素)。对于我的数据,如果我对@bar进行排序,$foo的值更可能出现在数组的开头而不是末尾。我想知道这是否有助于提高性能 换言之,对于上述代码,grep是否按顺序通过@bar检查$foo==$\u并在发现任何值为真时立即退出?或者它会在返回值之前检查@bar的每个元素吗?因为您使用的grep是在标量上下

我想了解一些关于Perl的
grep
函数如何工作的细节。我正在这样做:

if ( grep{ $foo == $_ } @bar ) {
  some code;
}
假设
@bar
很大(数十万个元素)。对于我的数据,如果我对
@bar
进行排序,
$foo
的值更可能出现在数组的开头而不是末尾。我想知道这是否有助于提高性能


换言之,对于上述代码,
grep
是否按顺序通过
@bar
检查
$foo==$\u
并在发现任何值为真时立即退出?或者它会在返回值之前检查
@bar
的每个元素吗?

因为您使用的
grep
是在标量上下文中,所以它会返回匹配元素的数量。要计算这一点,Perl无论如何都必须访问每个元素,因此排序不太可能从这个角度提高性能。

grep
不会短路,因此元素的顺序无关紧要

虽然List::MoreUtils的
first
会短路,但在调用之前,必须将整个列表放在堆栈上

这将是最好的:

for (@bar) {
   if ($foo == $_) {
      some code;
      last;
   }
}

更新了:我最初迭代了索引,因为它使用了O(1)内存,但是(@bar)的
(与(列表)
相反)正如ysth提醒我的那样。

关于你的问题

对于我的数据,如果我对@bar进行排序,$foo的值更可能出现在数组的开头,而不是末尾。我想知道这是否有助于提高性能

如果列表按数字顺序排序,则可以写入

sub contains {
  my ($list, $item) = @_;
  for (@$item) {
    return $_ == $item if $_ >= $item;
  }
  return !1;
}

some_code() if contains(\@bar, $foo);

在您的示例中,grep将迭代整个数组,而不管匹配了多少个元素


如果您能够保持此数组的排序,那么使用二进制搜索搜索值会更快。您还可以将数组转换为散列(key=array元素)并使用固定时间进行检查,但这会消耗额外的内存。

这取决于具体情况。A
grep{$x==$\u}@A
不会从分支预测中受益,但是
grep{$x<$\u}@A
会受益

#!/usr/bin/env perl

use strict;
use warnings;

use Time::HiRes qw( clock_gettime CLOCK_MONOTONIC );

use constant MIN => 0;
use constant MAX => 1000;
use constant AVG => int(MIN  + (MAX - MIN) / 2);
use constant N_LOOPS => 40000;
use constant ARRAY_LEN => 10000;

## is grep faster for sorted arrays?

##
## RANDOM ARRAY VALUES
##
my $n = 0;
my @a = map { int(rand() * (MAX - MIN) + MIN) } 1 .. ARRAY_LEN;
my $duration = -clock_gettime ( CLOCK_MONOTONIC );
for(my $i = 0; $i < N_LOOPS; $i++) {
    $n += grep { AVG < $_ } @a;
}
$duration += clock_gettime ( CLOCK_MONOTONIC );
print "duration: $duration secs, n = $n".$/;

##
## PREDICTABLE ARRAY VALUES
##
$n = 0;
@a = sort {$a <=> $b} @a;
$duration = -clock_gettime ( CLOCK_MONOTONIC );
for(my $i = 0; $i < N_LOOPS; $i++) {
    $n += grep { AVG < $_ } @a;
}
$duration += clock_gettime ( CLOCK_MONOTONIC );
print "duration: $duration secs, n = $n".$/;

## and now we try to eliminate side effects by repeating

##
## RANDOM ARRAY VALUES
##
$n = 0;
@a = map { int(rand() * (MAX - MIN) + MIN) } 1 .. ARRAY_LEN;
$duration = -clock_gettime ( CLOCK_MONOTONIC );
for(my $i = 0; $i < N_LOOPS; $i++) {
    $n += grep { AVG < $_ } @a;
}   
$duration += clock_gettime ( CLOCK_MONOTONIC );
print "duration: $duration secs, n = $n".$/; 

##
## PREDICTABLE ARRAY VALUES
##
$n = 0;
@a = sort {$a <=> $b} @a;
$duration = -clock_gettime ( CLOCK_MONOTONIC );
for(my $i = 0; $i < N_LOOPS; $i++) {
    $n += grep { AVG < $_ } @a;
}   
$duration += clock_gettime ( CLOCK_MONOTONIC );
print "duration: $duration secs, n = $n".$/; 
#/usr/bin/env perl
严格使用;
使用警告;
使用时间::雇佣qw(时钟时间单调);
使用常数MIN=>0;
使用常量MAX=>1000;
使用常量AVG=>int(MIN+(MAX-MIN)/2);
使用常数N_循环=>40000;
使用常量数组_LEN=>10000;
##grep对于排序数组是否更快?
##
##随机数组值
##
我的$n=0;
my@a=map{int(rand()*(MAX-MIN)+MIN)}1。。阵列透镜;
my$duration=-clock\u gettime(clock\u单调);
对于(我的$i=0;$i
结果是:

duration: 27.7465513650095 secs, n = 199880000 <-- unsorted
duration: 26.129752348992 secs, n = 199880000  <-- sorted
duration: 28.3962040760089 secs, n = 202920000 <-- unsorted
duration: 26.082420132996 secs, n = 202920000  <-- sorted

持续时间:27.746551365095秒,n=199880000好问题。我认为,您不需要使用
grep
,而需要使用
for()
来尽早退出,请参见下面我的评论<如果我正确理解你的意思,CPAN上的代码>列表::MoreUtils
可以做你想做的事task@loldop你应该把这当作一个答案。似乎
firstidx
可以做我想做的事情。@user1937198:不,在演示的用法中,它返回条件为真的元素计数,如果不可能出现side,它可以优化为在布尔上下文中提前停止effects@itzy你会做更多的测试吗?如果是这样的话,创建一个散列可能是个好主意。在这种情况下可能会有所帮助?不sure@loldop我认为是这样!我对这个模块不是特别熟悉,但它似乎构造得很好,List::MoreUtils::any在这里很有用。
grep
检查所有列表元素,而不考虑上下文。它只返回列表上下文中的列表和标量上下文中的计数
first
和类似选项在这里是一个错误的选择,因为整个列表被复制到函数的参数中,这对于成千上万的标量来说是一个大问题。最好的解决方案是一个简单的
for
循环,在满足条件后使用
last
。如果匹配接近列表的开头,并且可以检测到循环超出了任何可能的匹配范围,那么这样的循环将更快终止。您确定使用索引比直接使用元素快吗?@TLP不会将数组放在堆栈上。记忆方面,是的。速度,可能比单独的额外操作慢一点点。因为在数组上,不会将数组放在堆栈上
第一个
很可能会更快,即使使用堆栈副本也是如此。内存可能是一个问题,但“如果它只适合内存一次,那么它将适合两次”是一个足够好的第一近似值。@hobbs,为什么做得更多会更快?!它做同样的事情,再加上一大堆函数调用。如果对列表进行排序,您可以使用bsearch更快地找到结果。另外,
返回
返回空列表或未定义,而
返回$item
返回1或“”,因此此子项有4个不同的