LINUX.ORG.RU

grep с остановкой при первом совпадении?

 , , ,


0

3

Есть ли для perl'а какая-либо реализация логики grep, при которой если grep проверяется в булевом контексте, то первое же совпадение приведёт к завершению перебора входного списка?

Просто косячить эту логику циклом for - как-то некошерно, да и короткая форма работает уродливо:

my $f;
my %hsh=('a'..'z');
my $arr=[qw/b c d/]
exists $hsh{$_} and $f=$_, last for @{$arr};

Делать из $arr сначала временный хеш, а потом по нему искать пересечения с %hsh - тоже как-то криво (мягко скажем).

Идеальным был бы вариант:

perl -E 'print "hello\n" if grep { say; $_&1 } 1..31'

Но он печатает всё от 1 до 31, хотя уже первого элемента списка достаточно для true в if'е. Как известно, в grep не работают никакие last'ы, break'и, next'ы. Возможно, есть уже некий кусок XS'ки, исправляющий эту досадную недоработку? Если уж в этом случае grep зачем-то перебирает всё подряд, то чего бы тогда в and и or не вычислять всегда левую и правую части выражения, просто «шоб було»?

Запостил этот вопрос на SO: https://stackoverflow.com/questions/47416807/avoid-grepping-all-in-boolean-co...

★★★★★

Ответ на: комментарий от disarmer

Результат:

Preparing sampling matrix 100000x1000...
done
Benchmarking...
Benchmark: timing 100000 iterations of List::Util, Native::For, Native::Grep...
List::Util:  0 wallclock secs ( 0.25 usr +  0.00 sys =  0.25 CPU) @ 400000.00/s (n=100000)
            (warning: too few iterations for a reliable count)
Native::For:  0 wallclock secs ( 0.12 usr +  0.00 sys =  0.12 CPU) @ 833333.33/s (n=100000)
            (warning: too few iterations for a reliable count)
Native::Grep:  0 wallclock secs ( 0.31 usr +  0.00 sys =  0.31 CPU) @ 322580.65/s (n=100000)
            (warning: too few iterations for a reliable count)

Для бенчмарка вот такого:

#!/usr/bin/perl
use 5.16.1;
use constant {
    DFLT_ITER_COUNT => 1000,
    DFLT_VEC_LENGTH => 100,
};
use strict;
use Time::HiRes;
use Benchmark qw(:all);
use List::Util qw(first);
use List::Util::XS 1.20;
use Getopt::Std;

getopts('i:l:', \my %opt);
my ($iterCount, $vecLength) = ($opt{'i'} || DFLT_ITER_COUNT, $opt{'l'} || DFLT_VEC_LENGTH);

say "Preparing sampling matrix ${iterCount}x${vecLength}...";
my @rndNums=map { [map { int(rand($vecLength)) } 1..$vecLength] } 1..$iterCount;
say 'done';

say 'Benchmarking...';
timethese($iterCount, {
    'List::Util' => sub {
        my $r=first { ($_&3) == 0  } @{$rndNums[int(rand($iterCount))]}
    },
    'Native::For' => sub {
        my $r;
        !($_&3) and $r=$_, last for @{$rndNums[int(rand($iterCount))]};
        $r
    },
    'Native::Grep' => sub {
        my $r;
        LABEL: { grep { !($_&3) and $r=$_, last(LABEL) } @{$rndNums[int(rand($iterCount))]} }
        $r
    },
});

DRVTiny ★★★★★ ()
Последнее исправление: DRVTiny (всего исправлений: 1)
Ответ на: комментарий от DRVTiny

У тебя тестовые данные для бенчмарков разные из-за этого:

@{$rndNums[int(rand($iterCount))]};

Подготовь список индексов перед бенчмарком. Ведь для такого поиска время линейно растет чем дальше элемент.

KennyMinigun ★★★★★ ()