titaniumdecoy
titaniumdecoy

Reputation: 19251

Why doesn't my Perl grep return the first match?

The following snippet searches for the index of the first occurrence of a value in an array. However, when the parentheses around $index are removed, it does not function correctly. What am I doing wrong?

my ($index) = grep { $array[$_] eq $search_for } 0..$#array;

Upvotes: 18

Views: 11896

Answers (6)

user142917
user142917

Reputation: 11

Also, I think using grep just to find the first instance is a little inefficient since it still has to walk through and run the callback on every element of the array. Especially if your array is long, you may be better off writing a loop or else using List::MoreUtils as mentioned above.

Upvotes: 1

Plutor
Plutor

Reputation: 2897

grep returns a list. When you put the scalar variable name inside parentheses, Perl treats that whole l-value as a list, so it assigns the first value in the list to that variable.

If you had other scalars in the parentheses, you'd get the second, third, etc values from grep's return array in them:

my ($index, $foo, $bar) = grep { $array[$_] eq $search_for } 0..$#array;

Upvotes: 1

Chas. Owens
Chas. Owens

Reputation: 64909

The grep function behaves differently in list context and scalar context. This documented in perldoc -f grep:

Evaluates the BLOCK or EXPR for each element of LIST (locally setting $_ to each element) and returns the list value consisting of those elements for which the expression evaluated to true. In scalar context, returns the number of times the expression was true.

You can duplicate this on your own with the poorly named wantarray function:

sub my_grep {
    my $sub = shift;
    my @return;
    for my $item (@_) {
        push @return if $sub->($item);
    }
    return @return if wantarray;
    return scalar @return;
}

Upvotes: 5

Sinan Ünür
Sinan Ünür

Reputation: 118118

I think you are looking for first_index from List::MoreUtils:

use List::MoreUtils qw( first_index );

# ...

my $index = first_index { $_ eq $search_for } @array;

Upvotes: 7

innaM
innaM

Reputation: 47829

The parentheses provide a list context for grep. grep will then actually return the list of elements for which the expression was true and not just the number of times the expression was true.

Upvotes: 8

Michael Carman
Michael Carman

Reputation: 30831

The parentheses change the context in which the grep is evaluated from scalar context to list context. In scalar context grep returns the number of times the expression was true. In list context it returns the elements for which the expression was true.

The following highlights the difference context makes:

my   $x   = grep {/foo/} @array;  # the number of things that match /foo/
my  ($x)  = grep {/foo/} @array;  # the first thing that matches /foo/
my  @foo  = grep {/foo/} @array;  # all the things that match /foo/
my (@foo) = grep {/foo/} @array;  # all the things that match /foo/

Upvotes: 44

Related Questions