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

Writing a code snippet to create a list of filenames with no extensions given a list of filenames with extensions, i ran into some weirdness. When i create the second array using a grep, the orginal array also changes. Whilst i would expect this behavior with map, it suprised me with grep. My only guess is that both variables are the same array, but they are arrays (not array references) so how would that be?
#!/usr/bin/perl -w use strict; my @a = qw(bob.txt bob jack.bmp dave.txt mark); my @b = grep{$_=(m|^(.*)\.(.*?)$|)?$1:$_}@a; print join(', ',@a)."\n".join(', ',@b);
(When code is executed, @a and @b are both contain the values of the grep)

Replies are listed 'Best First'.
Re: Grep is changing the original list?
by merlyn (Sage) on Apr 17, 2001 at 20:46 UTC
    perldoc -t -f grep yields (in part)...
    Note that, because $_ is a reference into the list value, it can be used to modify the elements of the array. While this is useful and supported, it can cause bizarre results if the LIST is not a named array. Similarly, grep returns aliases into the original list, much as a for loop's index variable aliases the list elements. That is, modifying an element of a list returned by grep (for example, in a foreach, map() or another grep()) actually modifies the element in the original list. This is usually something to be avoided when writing clear code.

    -- Randal L. Schwartz, Perl hacker

Re: Grep is changing the original list?
by arturo (Vicar) on Apr 17, 2001 at 20:50 UTC

    grep won't stop you from modifying the arguments in the BLOCK. The path to wisdom lies in the realization that = is the assignment operator ...

    That issue aside, if what you want are the members of @a that *lack* extensions, you should match things that don't end in a . followed by some word characters (something like that, anyhow). So try:

    my @b = grep { !/\.\w+$/} @a;

    Philosophy can be made out of anything. Or less -- Jerry A. Fodor

Re: Grep is changing the original list?
by Masem (Monsignor) on Apr 17, 2001 at 20:47 UTC
    From grep:
    Note that, because $_ is a reference into the list value, it can be used to modify the elements of the array.
    Why would grep work this way? Well, both map and grep are doing the same sort of thing: take a list, do some processing, and put something into a new array. With map, it puts the result from the processing, and for grep it puts the current item if the result is not false. So both probably share the same bits of code to do this.


    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
      Well, I'd venture instead that it's because it's that way for foreach, and it's that way in foreach so that we can stay efficient. Better a reference than a copy.

      -- Randal L. Schwartz, Perl hacker

Re: Grep is changing the original list?
by kschwab (Vicar) on Apr 17, 2001 at 20:48 UTC
    From "perldoc -f grep":

    Note that, because $_ is a reference into the list value, it can be used to modify the elements of the array.

    Perhaps you meant  $_=~rather than $_= ? (Your script seems to work as you intended by adding the tilde :)

Re: Grep is changing the original list?
by Malkavian (Friar) on Apr 17, 2001 at 20:49 UTC
    merlyn just pipped me to the post on that one.
    Also, just typing in grep in the search box gives you all the info too. I've got caught out many times by not going back to the docs and re-reading, which is why I bought myself a tshirt with 'RTFM' on it.. So every time someone asks me, what it means, I get a reminder too.. :)

    Malk
Re: Grep is changing the original list?
by iamcal (Friar) on Apr 17, 2001 at 20:58 UTC
    excellent help - thanks alot everyone. i've switched to a for with pushes instead:

    push @b,(m|^(.*)\.\w+$|)?$1:$_ for(@a);

    The reason i'm using the assignment operator (and i did mean to) is so that files without an extension get left in as is (Note the ? operator). I'm guessing there must be a better (faster) way to do this though? Thanks.