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

I'm trying to test two different pieces of data at once using an unless (grep .. grep) and was wondering if it's possible. Some of you may tell me to try such a thing before asking, but that's why I'm here. I am getting unexpected results and after thorough testing, I can't see where else the mistake lies.
unless ( grep{$test1 eq $_} @accepted1 && grep{$test2 eq $_} @accepted +2) {
My tests to see what's going on are simple. I print both $test1 and $test2 along with a foreach(@array) for both sets
print "you had test1: $test1<br>"; foreach(@test1) { print "$_<br>"; } print "<br>you had test2: $test2<br>"; foreach(@test2) { print "$_<br>"; }
And the tests come back positive. Test1 matches perfectly with an item that worked in the first array (this was working properly until I decided to add the 2nd test) and Test2 matches perfectly with an item from it's array too. But something is making it fail.

Is this grep doing something I'm not seeing? Only idea I can come up with is it's testing just the first item in both arrays and if they don't match according to what we want, it says it doesn't work instead of going over every array item.

Thanks for your help, wise monks.



"Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

sulfericacid

Replies are listed 'Best First'.
Re: multiple greps at once-- possible?
by jdporter (Paladin) on Apr 08, 2004 at 15:03 UTC
    Try it with and instead of &&

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

      Wow, that was certainly a fast and accurate reply :) Thank you for your help, it works perfectly now!


      "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

      sulfericacid
Re: multiple greps at once-- possible?
by dragonchild (Archbishop) on Apr 08, 2004 at 15:18 UTC
    To keep this problem from happening again, you might want to look at Quantum::Superpositions, specifically the any() and all() functions. List::Utils has some useful functions, as well. The reason I point you to them is that they will work better with grep than 'and' or '&&'.

    Another solution to your direct problem is to put the "grep { } @arr" in parentheses.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

      To keep this problem from happening again, you might want to look at Quantum::Superpositions, specifically the any() and all() functions.
      Eh, no, you don't. Quantum::Superpositions is extremely slow. There's no justification for using Quantum::Superpositions, just to save typing a pair of parenthesis. Here's a benchmark:
      #!/usr/bin/perl use strict; use warnings; use Benchmark qw /cmpthese/; use Quantum::Superpositions; our @data1 = "aa" .. "zz"; our @data2 = "AA" .. "ZZ"; my %queries = ( middle => [$data1 [@data1 / 2], $data2 [@data2 / 2]], first => [$data1 [0], $data2 [0]], not => ["feeble", "fnord"], ); foreach my $type (qw /middle first not/) { warn "Running '$type' test.\n"; our ($q1, $q2) = @{$queries {$type}}; our ($a_g, $a_q); cmpthese -1 => { grep => '$a_g = grep ({$q1 eq $_} @data1) && grep ({$q2 eq $_} @data2)', QS => '$a_q = $q1 eq any (@data1) && $q2 eq any (@data2)' +, }; die "Unequal" if $a_g xor $a_q; print "\n"; } __END__ Running 'middle' test. Rate QS grep QS 24.5/s -- -99% grep 2376/s 9588% -- Running 'first' test. Rate QS grep QS 24.1/s -- -99% grep 2400/s 9869% -- Running 'not' test. Rate QS grep QS 48.6/s -- -99% grep 5744/s 11719% --

      Abigail

      Q::S is not a small module. While any and all can be implemented with a lot less code, the 'Quantum' part of is what makes Q::S so large. IMHO, using it almost anywhere except as a toy is going to be massive overkill.

      ----
      : () { :|:& };:

      Note: All code is untested, unless otherwise stated

      Another solution to your direct problem is to put the "grep { } @arr" in parentheses.
      Indeed... personally I've never been one to favor the "new" style of only using parens when only absolutely necessary. Precendence rules are tricky (i.e. the && vs. and). By using parens I can at least explicitly tell the compiler what I want (and it helps visually as well, IMHO). I find
      open(IN, "foo") || die "...";
      much easier to read than
      open IN, "foo" or die "...";
      Michael
Re: multiple greps at once-- possible?
by Roy Johnson (Monsignor) on Apr 08, 2004 at 16:26 UTC
    Since you're not actually using the set of values generated by each grep, but only want to test whether there is such a match, it might (depending on the sizes of the arrays and how often called) be useful to write your tests as a short-circuiting foreach:
    my @a = ('a'..'j'); my @b = ('j'..'z'); my $a = 0; if (ss_grep('b', \@a) and ss_grep('k', \@b)) { print "Found b and k\n"; } print "$a iterations\n"; sub ss_grep { my ($test, $aref) = @_; for (@$aref) { ++$a; return 1 if $_ eq $test; } 0; }

    The PerlMonk tr/// Advocate
      Since you're not actually using the set of values generated by each grep, but only want to test whether there is such a match, it might (depending on the sizes of the arrays and how often called) be useful to write your tests as a short-circuiting foreach:
      Whether 'grep' or a short circuiting for is faster doesn't depend on how often you call it (although it matters whether it's significant). The size of the array plays a role, but what's more important is whether there is a match, and if there is, where the first match is. 'grep' wins if there's no match, or if the match is at the end. In the past, I've done some benchmarking, and found that the short circuit method was faster if the first match was to be found in about the first two-thirds of the list. I've also found noticable differences in the cut-off point when doing exact matching (==) or regex matching (=~) - differences I've not been able to explain.

      But with different versions of Perl, you might get different results. My point is that deciding whether the replace a grep with a short-circuiting for loop is not an easy decision.

      Abigail

        Hmm... It's intresting to combine the both approaches
        my @a = qw (a d c d s f ss eef e as d); GREP: { grep{ $_ eq 'd' and next GREP } @a; print 'not match'; last GREP; } continue { print 'match'; last GREP; }
        I'll benchmark this code later

        Update: It' was a bad idea. This code is very slow. The two fastest are:

        #... grep_line => sub { my($test, $ra) = @_; grep { $_ eq $t and return 1 } @$ra; return; }, for_line => sub { my($test, $ra) = @_; $test eq $_ and return 1 foreach @$ra return; } #...
        Whether 'grep' or a short circuiting for is faster doesn't depend on how often you call it
        Correct, but whether it is usefully faster might.
        what's more important is whether there is a match
        Well, yes. I did assume that we would expect to find a match rather often.

        The PerlMonk tr/// Advocate