Professional Web Applications Themes

Grep Weirdness - PERL Beginners

Greetings, Encountering some unexpected behavior illustrated in the code snippet below. In the first foreach loop Seems like when I check for a match between gid306 and the contents of the the ACTIVES array I get an erroneous hit. But as shown in the second foreach loop if I remove gid and just try to match the number 306 it correctly determines there is no match. Is this a bug in Perl or in my understanding? Thanks John Kent #!/usr/bin/perl my($DEBUG) = 1; my(ACTIVE) = qw {gid240 gid278 gid301}; my(LOGGED) = qw {gid306 gid240 gid278 gid301}; # This doesn't work, ...

  1. #1

    Default Grep Weirdness

    Greetings,

    Encountering some unexpected behavior illustrated in the
    code snippet below.

    In the first foreach loop
    Seems like when I check for a match between
    gid306 and the contents of the the ACTIVES array
    I get an erroneous hit.

    But as shown in the second foreach loop
    if I remove gid and just try to match the number
    306 it correctly determines there is no match.

    Is this a bug in Perl or in my understanding?

    Thanks

    John Kent


    #!/usr/bin/perl

    my($DEBUG) = 1;

    my(ACTIVE) = qw {gid240 gid278 gid301};
    my(LOGGED) = qw {gid306 gid240 gid278 gid301};

    # This doesn't work, finds a match for every item in
    # LOGGED, seems to be matching on "gid" but ignoring the number
    foreach (LOGGED){
    unless (grep /$_/,ACTIVE){
    print "No Match for $__\n" if ($DEBUG == 1);
    #do something here with what didn't match";
    } else {
    print "found $_ in ACTIVES\n" if ($DEBUG == 1);
    }
    }


    print "\nNew test\n";

    # This works!!!
    foreach (LOGGED){
    my($match) = $_;
    # Remove the gid from the term to look for
    $match =~ s/gid//g;

    unless (grep /$match/,ACTIVE){
    print "No Match for $_\n" if ($DEBUG == 1);
    # do something here with what didn't match\n";
    } else {
    print "found $_ in ACTIVES\n" if ($DEBUG == 1);
    }
    }


    Results:
    [webuserweb2 cgi-bin]$ ./test_grep.pl
    found gid306 in ACTIVES
    found gid240 in ACTIVES
    found gid278 in ACTIVES
    found gid301 in ACTIVES

    New test
    No Match for gid306
    found gid240 in ACTIVES
    found gid278 in ACTIVES
    found gid301 in ACTIVES
    Mr. Guest

  2. #2

    Default Re: Grep Weirdness

    Mr. John Kent wrote: 

    <snip>
     

    You missed that when using grep(), $_ is set locally to each LIST
    element, i.e. in that context $_ does not represent the element in the
    LOGGED array.
     

    Consequently, that is always true.

    To get rid of the confusion, you can make use of a named variable in
    the foreach loop:

    foreach my $logged (LOGGED) {
    unless (grep { $logged eq $_ } ACTIVE) {
    print "No Match for $logged\n";
    } else {
    print "found $logged in ACTIVES\n";
    }
    }

    --
    Gunnar Hjalmarsson
    Email: http://www.gunnar.cc/cgi-bin/contact.pl
    Gunnar Guest

  3. #3

    Default Re: Grep Weirdness

    Kent, Mr. John (Contractor) wrote: 

    Hello,
     


    always put
    use strict;
    use warnings;
    here. that will solve 99% of the problems for you by telling you what is
    off ;p
     

    No need to surround those variable/array neame with ()

    my $DEBUG = 1;
    my ACTIVE = ...

    Also it works but is confusing to use curly braces qith qw.

    my LOGGED = qw(gid306 gid240 gid278 gid301);

    is better because there's no need to guess if you're trying to do an
    array of items or an array with one hashref in it.

     

    Your $_ are going out of scope. the grep() $_ is an item in ACTIVE and
    in that case will always be true.

    Also the unless and else is a bit odd

    Try nameing the variables:
     

    I think you meant $_ not $__ strict and warnings would have told you
    about that.
     

    Again, no () are really necessary
    Also you can shorten it to if $DEBUG; then set $DEBUG to 0 when you
    don't want that output.
     

    Here it is with all the changes, (necessary and cosmetic):

    #!/usr/bin/perl

    use strict;
    use warnings;

    my $DEBUG = 1;
    my ACTIVE = qw(gid240 gid278 gid301);
    my LOGGED = qw(gid306 gid240 gid278 gid301);

    for my $logd(LOGGED) {
    if(grep { $logd eq $_ } ACTIVE) {
    print "Found $logd in ACTIVES\n" if $DEBUG;
    } else {
    print "No match for $logd\n" if $DEBUG;
    }
    }

    __END__

    No match for gid306
    Found gid240 in ACTIVES
    Found gid278 in ACTIVES
    Found gid301 in ACTIVES

    HTH :) Lee.M - JupiterHost.Net
    JupiterHost.Net Guest

  4. #4

    Default RE: Grep Weirdness

    Kent, Mr. John (Contractor) wrote:
    .... 

    Others have explained the localization of $_ issue. It's not important for
    such a small data set, but if ACTIVE is large, you can improve performance
    by doing a hash lookup instead of a grep():

    use strict;

    my(ACTIVE) = qw {gid240 gid278 gid301};
    my(LOGGED) = qw {gid306 gid240 gid278 gid301};

    my %h; h{ACTIVE} = ();
    print exists $h{$_} ? "Found" : "No match for", " $_\n" for LOGGED;
    Bob Guest

  5. #5

    Default Re: Grep Weirdness

    Mr. John \ Kent wrote:
     

    I don't think you're doing what you think you're doing.
     

    You're not only removing the gid. You're also copying the search variable
    into another variable. See below.
     

    A but in Perl? Perish the thought! :-)
     

    Here, you don't specify a name for the scalar in the foreach loop. Perl
    therefore defaults to assigning the variable to $_.
     

    When you supply grep with a list, grep walks through and assigns each scalar
    from the list to a default variable and checks to see if the statement is
    true. It essentially creates its own little loop. Guess which variable it
    uses to assign the values from the list? Yep. It uses $_. That, of
    course, clobbers the value that got assigned to $_ in the foreach loop. So
    what you're actually doing is searching the array ACTIVE for each scalar
    in the array ACTIVE, not searching for each member of the array LOGGED.
    Since you're searching the array for members of itself, of course it finds
    every one.
     

    Here, you assign the value of $_ to $match BEFORE it gets clobbered by grep.
     

    $_ gets clobbered here but you aren't searching for $_ anymore, you're
    searching for $match. The fact that you're removing the "gid" from the
    search is irrelevant.

    Of course the real issue here is overusing the default variables. Default
    variables are handy and invaluable in some cirstances but they can bite
    you if you're not careful, as you just found out. Unless there's a need to
    use default variables, your code is generally more readable using explicit
    variables. And, of course, you eleminate a potential source of errors. To
    fix the program, just add an explicit variable to your loop:

    foreach $log(LOGGED){
    unless (grep /$log/,ACTIVE){
    print "No Match for $log\n" if ($DEBUG == 1);
    #do something here with what didn't match";
    } else {
    print "found $log in ACTIVES\n" if ($DEBUG == 1);
    }
    }

    Hope this helps,

    Dan


    Dan Guest

Similar Threads

  1. grep in php
    By Papa in forum PHP Development
    Replies: 2
    Last Post: March 31st, 12:46 PM
  2. grep with [ ] brackets ... ?
    By Luke Bakken in forum PERL Beginners
    Replies: 3
    Last Post: September 23rd, 02:14 PM
  3. grep with ftp
    By Shaunn Johnson in forum PERL Beginners
    Replies: 9
    Last Post: September 12th, 06:51 AM
  4. Help with grep
    By Kevin in forum Sun Solaris
    Replies: 3
    Last Post: September 4th, 07:11 AM
  5. grep
    By Anders Bystrup in forum PERL Miscellaneous
    Replies: 3
    Last Post: August 7th, 09:36 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139