adrive has asked for the wisdom of the Perl Monks concerning the following question:

Hi, i'm a little confused with the following examples of grep, all these while i thought grep's 2nd parameter is supposed to store array only but this shows the difference :
#!/usr/bin/perl use strict; use warnings; my @coins = ("twenty","thirthy","fourthy"); my $key; my $value; my ($index) = grep($coins[$_] eq "fourthy", 0 .. $#coins); print "\nthe index of 'fourthy' is " . $index . "\n"; my @index2 = grep($_ ne "fourthy", @coins); print "\nnone 'fourthy' elements are " . join(",",@index2);

question :
1. why ($index) needs to be written in bracket for the grep to return an appropriate index number?

2. both grep's $_ symbol seems to work differently..is this dependent on the 2nd parameter of grep? one works as a key, another as a value!

3. why can grep's 2nd parameter be either an array or sequence of array index?

Replies are listed 'Best First'.
Re: grep usage confusion
by ikegami (Patriarch) on Oct 14, 2008 at 03:27 UTC

    why ($index) needs to be written in bracket for the grep to return an appropriate index number?

    my $index returns a scalar and thus creates a scalar context.
    In scalar context, grep returns the number of matching items (which is not what you want).

    my ($index) returns a list and thus creates a list context.
    In list context, grep returns the matching items (where your code assigns the first one).

    why can grep's 2nd parameter be either an array or sequence of array index?

    You are mistaken in thinking the 2nd parameter is an array or sequence. You are passing a whole list of arguments.

    Since
    0 .. $#coins
    returns
    0, 1, 2
    then
    my ($index) = grep($coins[$_] eq "fourthy", 0 .. $#coins);
    is the same as
    my ($index) = grep($coins[$_] eq "fourthy", 0, 1, 2);

    Since
    @coins
    returns
    $coins[0], $coins[1], $coins[2]
    then
    my @index2 = grep($_ ne "fourthy", @coins);
    is the same as
    my @index2 = grep($_ ne "fourthy", $coins[0], $coins[1], $coins[2]);

    This has nothing to do with grep. The expressions @coins and 0..$#coins are evaluated before grep is even called.

    >perl -le"$,=','; print @ARGV" twenty thirty forty twenty,thirthy,fourthy >perl -le"$,=','; print 0..$#ARGV" twenty thirty forty 0,1,2

    By the way, you misspelled "thirty" and "forty".

    both grep's $_ symbol seems to work differently..is this dependent on the 2nd parameter of grep? one works as a key, another as a value!

    The first time the first argument is executed, $_ is aliased to the second argument.
    The second time the first argument is executed, $_ is aliased to the third argument.
    The third the first argument is executed, $_ is aliased to the fourth argument.
    And so on.

    There's no such things as keys and values, just the list of arguments you passed to grep.

Re: grep usage confusion
by GrandFather (Saint) on Oct 14, 2008 at 03:20 UTC

    grep returns a list. In scalar context (my $index = ... for example) a list returns the count of elements in the list. Writing my ($index) = ... makes $index the first member of a one element list and grep's return list is in list context so the first element of grep's list is assigned to the first element of the left hand side list - $index.

    $_ is always an alias to each element in the list for grep, map and in a for loop (whatever you use as the loop variable is the alias in the case of a for loop). In the first case you provide a list of numbers: 0 .. $#coins (indexes into the array). In the second case you provide a list of elements - the elements of @coins.

    grep's "second parameter" is always a list. You provided two different lists in your sample code. The range operator generates a list.

    Note that most often grep expressions are written using a block: grep {$_ ne "fourthy"} @coins.


    Perl reduces RSI - it saves typing
      grep returns a list. In scalar context (my $index = ... for example) a list returns the count of elements in the list.
      No, it doesn' work that way. There is no list in scalar context - it is, afterall, scalar context. Lists only exist in list context. It is grep that returns the number of matches in scalar context.

      If it were true that functions returned lists, and lists in scalar context return the count of elements, then none of the following would work the way they do now:

      my $formatted_time = localtime; my $last_element = (10, 11, 12); my $package = caller; my $success = stat "/etc/passwd"; ...
Re: grep usage confusion
by rovf (Priest) on Oct 14, 2008 at 08:36 UTC
    my @index2 = grep($_ ne "fourthy", @coins);

    I guess you meant

    my @index2 = grep {$_ ne "fourthy"} @coins);
    or
    my @index2 = grep('$_ ne "fourthy"', @coins);
    because in the way you have written it, it would not work (grep needs to eval the first argument (block or string) repeatedly for each item in the argument list).

    -- 
    Ronald Fischer <ynnor@mm.st>

      No, grep doesn't eval strings of Perl code. You can give it a block or you can give it an expression (not a string). Your first example does the same thing as the original code (which does work) while your second example returns all of @coins since the string '$_ ne "fourthy"' is a true value.

      - tye        

        grep doesn't eval strings of Perl code.

        Oops, you are right! My mistake!

        as the original code (which does work)
        Thanks for pointing that out. Interesting. I was not aware of that usage of grep (always used it with a block).

        -- 
        Ronald Fischer <ynnor@mm.st>