Eugene Barsky
Eugene Barsky

Reputation: 6002

Filtering elements of an array with elements of another array in Perl 6

I want to filter elements of @array which begin with elements of @search:

my @array = "aaaaa" .. "fffff";
my @search = "aaaa" .. "cccc";
.put for @array .grep: /^ @search /;

The problem is it takes 19 seconds. So, I 'precompile' the regex for grep, and the whole program looks like this:

my @array = "aaaaa" .. "fffff";
my @search = "aaaa" .. "cccc";

my $search = "/@search.join('|')/".EVAL;

.put for @array .grep: * ~~ /^ <$search> /;

Now it takes 0.444s.

The question: is there a built-in Perl 6 method to do such things? Something like inserting a junction into a regex...

Upvotes: 7

Views: 909

Answers (3)

jjmerelo
jjmerelo

Reputation: 23517

For this kind of search, you can easily use index

say (@array X @search).grep( { defined($^a[0].index($^a[1])) } )
    .grep( { $^a[0].index($^a[1]) == 0 } );

This creates a list of Cs, and seeks the second element of the pair within the first; it returns only the list of those that appear in the first position. defined is needed because it will return Nil if it does not find it. It's not faster than your alternative above, but it's in the same ballpark, with 0.03 system time and ~ 0.30 seconds

Upvotes: 1

H&#229;kon H&#230;gland
H&#229;kon H&#230;gland

Reputation: 40748

You could try to speed this up by assembling the regexes.

I am not sure how to do this using pure Perl 6 but Regexp::Assemble is a Perl 5 module that can do this for Perl 5 regexes. You can use Perl 5 modules in Perl 6 code by appending a :from<Perl5> (without a preceding space) to a use statement and then accessing its exported symbols (classes, objects, routines, etc.) as if it was a Perl 6 module:

use v6;

use Regexp::Assemble:from<Perl5>;

my @array = "aaaaa" .. "fffff";
my @search = "aaaa" .. "cccc";
my $ra = Regexp::Assemble.new;
$ra.add( @search );
$ra.anchor_string_begin(1);
.put for @array.grep({so($ra.match( $_ ))});

Upvotes: 3

Scimon Proctor
Scimon Proctor

Reputation: 4558

my @array = "aaaaa" .. "fffff";
my @search = "aaaa" .. "cccc";
my $join = @search.join('|');
my $rx = rx{ ^ <{$join}> };

@array.grep( * ~~ $rx ).map( *.put )

You need to create the join string separately of it will evaluate the array join for each match. But the basically gives you what you want without using EVAL.

Upvotes: 4

Related Questions