Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

given-when construct unexpected behavior with arrays

by mantager (Sexton)
on Jun 07, 2012 at 13:06 UTC ( [id://974933]=perlquestion: print w/replies, xml ) Need Help??

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

Hi monks,

I'm trying to understand the behavior of the given-when construct
when I use an array reference in the "when" clause.
I saw a similar example in "Programming Perl - 4th ed."
but it's not doing what I expected.
I have this test case:

#!/usr/bin/env perl use v5.14; my @array = (0, 1000..10_000, 'abcd'); for my $element (qw/a ab abc 100 1000 10000 abcd/) { say "With grep:"; if (grep {/^$element$/} @array) { say "Element $element is there"; } else { say "Element $element is not there"; } say "With given-when:"; given ($element) { when (\@array) { say "Element $element is there"; } default { say "Element $element is not there"; } } say ""; }


It yields this result:

With grep: Element a is not there With given-when: Element a is there With grep: Element ab is not there With given-when: Element ab is there With grep: Element abc is not there With given-when: Element abc is there With grep: Element 100 is not there With given-when: Element 100 is not there With grep: Element 1000 is there With given-when: Element 1000 is there With grep: Element 10000 is there With given-when: Element 10000 is there With grep: Element abcd is there With given-when: Element abcd is there


The 'grep' gets it right every time.
The given-when construct finds elements 'a', 'ab' and 'abc' even if the're not in @array.

The funny thing is: if I remove the 0 from @array,
elements 'a', 'ab' and 'abc' are reported as "not there",
so the matching element seems to be the 0.
I'm rather puzzled.

Can someone explain this?

Thanks, bye.

Replies are listed 'Best First'.
Re: given-when construct unexpected bahavior wit arrays
by toolic (Bishop) on Jun 07, 2012 at 13:31 UTC
    warnings kicks out some warning messages:
    use warnings; ... With grep: Element a is not there With given-when: Argument "a" isn't numeric in smart match at z line 23. Element a is there With grep: Element ab is not there With given-when: Argument "ab" isn't numeric in smart match at z line 23. Element ab is there

      Thank you for pointing this out, I thought use v5.14 would enforce strict and warnings, but I found out it's not so.

        From use, only strict is used, not warnings:
        if the specified Perl version is greater than or equal to 5.11.0, strictures are enabled lexically as with use strict .
Re: given-when construct unexpected bahavior wit arrays
by Neighbour (Friar) on Jun 07, 2012 at 13:32 UTC
    With grep, you're using a regular expression to find matches, which means all numeric values are stringified and then compared (as string).
    You're almost there when you conclude that if you remove 0, elements 'a', 'ab' and 'abc' are not found anymore...
    Apparently given-when does 'a' == 0 (which is true, when converting the string 'a' to a numerical value, it yields 0).

    Edit: Aw, beaten to it by toolic :)
Re: given-when construct unexpected bahavior wit arrays
by Tanktalus (Canon) on Jun 07, 2012 at 14:38 UTC

    I have to admit, I find this surprising. Looking at the perldocs for perlop, I see that:

    Left Right Description and pseudocode ============================================================== += [...] Any ARRAY smartmatch each ARRAY element[3] like: grep { Any ~~ $_ } ARRAY
    Ok, that's fine. So now I need to check how Any ~~ $_ works. So, a little further down, I see:
    Left Right Description and pseudocode ============================================================== += [...] Any Num numeric equality like: Any == Num Num nummy[4] numeric equality like: Num == nummy undef Any check whether undefined like: !defined(Any) Any Any string equality like: Any eq Any
    Now, this is interesting. When we see a number on the right, we compare with ==, even if the left doesn't look like a number. That seems wrong. I would have expected:
    nummy[4] Num numeric equality like: nummy == Num
    Maybe a bug report should be opened :-) (Though as I understand it, there's a fair bit of consternation about the whole smart-match thing in the first place.) I would also expect that nummy would include objects with == overridden.

Re: given-when construct unexpected bahavior wit arrays
by brx (Pilgrim) on Jun 07, 2012 at 16:03 UTC

    As Tanktalus said in Re: given-when construct unexpected bahavior wit arrays, this seems not natural.

    Here, something doing what you want (I also change first grep with "eq"), but I feel bad with that trick :-)

    .
    #!/usr/bin/env perl use v5.14; my @array = (0, 1000..10_000, 'abcd'); for my $element (qw/a ab abc 100 1000 10000 abcd/) { say "With grep:"; if (grep {$element eq $_} @array) { say "Element $element is there"; } else { say "Element $element is not there"; } say "With given-when:"; given ("_$element") { when ([map {"_$_"} @array]) { say "Element $element is there"; } default { say "Element $element is not there"; } } say ""; }

      Thanks you all, monks.
      I tested the regexp vs "eq" thing in grep (using Benchmark qw/timethis/) and "eq" is waaaaaaay faster than the regexp :D

      As for the smart match thing, I found out that too, and it seems quite broken. What I find strange is that the various comments go back even to 2010, and I thought it should had been fixed by now (<-- feel free to fix the verbs in the previous sentence, I usually get lost in hypothetical sentences :P ).
      I'll benchmark the last solution proposed by brx, just out of curiosity.

      Thank you again!
      Cheers.

        Ok, this is the last testcase:

        #!/usr/bin/env perl # ex: set tabstop=4 noexpandtab: use v5.14; use warnings; use Benchmark qw/timethis/; my $count = shift || 10_000; sub grep_in_array { my ($element, @array) = @_; grep {$element eq $_} @array and return 1; return 0; } sub is_in_array { my ($element, @array) = @_; given ("_$element") { when ([map {"_$_"} @array]) { return 1; } } return 0; } my @array = (0..10_000, 'abcd'); for my $element (qw/a ab abc 0 1 10 100 1000 10000 10001 abcd/) { say "Test with: $element"; say "With grep:"; say sprintf("Element %s %s in array", $element, grep_in_array($elem +ent, @array) ? "is" : "is not"); say "With given-when:"; say sprintf("Element %s %s in array", $element, is_in_array($elem +ent, @array) ? "is" : "is not"); say "With grep:"; timethis($count, sub { grep_in_array($element, @array); }); say "With given-when:"; timethis($count, sub { is_in_array($element, @array); }); }

        And the winner is: grep

        Test with: a With grep: Element a is not in array With given-when: Element a is not in array With grep: timethis 1000: 2 wallclock secs ( 2.17 usr + 0.00 sys = 2.17 CPU) @ + 460.83/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.34 usr + 0.00 sys = 7.34 CPU) @ + 136.24/s (n=1000) Test with: ab With grep: Element ab is not in array With given-when: Element ab is not in array With grep: timethis 1000: 2 wallclock secs ( 2.20 usr + 0.00 sys = 2.20 CPU) @ + 454.55/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.66 usr + 0.00 sys = 7.66 CPU) @ + 130.55/s (n=1000) Test with: abc With grep: Element abc is not in array With given-when: Element abc is not in array With grep: timethis 1000: 2 wallclock secs ( 2.33 usr + 0.00 sys = 2.33 CPU) @ + 429.18/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.62 usr + 0.00 sys = 7.62 CPU) @ + 131.23/s (n=1000) Test with: 0 With grep: Element 0 is in array With given-when: Element 0 is in array With grep: timethis 1000: 2 wallclock secs ( 2.17 usr + 0.00 sys = 2.17 CPU) @ + 460.83/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.24 usr + 0.00 sys = 7.24 CPU) @ + 138.12/s (n=1000) Test with: 1 With grep: Element 1 is in array With given-when: Element 1 is in array With grep: timethis 1000: 2 wallclock secs ( 2.34 usr + 0.00 sys = 2.34 CPU) @ + 427.35/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.44 usr + 0.00 sys = 7.44 CPU) @ + 134.41/s (n=1000) Test with: 10 With grep: Element 10 is in array With given-when: Element 10 is in array With grep: timethis 1000: 2 wallclock secs ( 2.27 usr + 0.00 sys = 2.27 CPU) @ + 440.53/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.57 usr + 0.01 sys = 7.58 CPU) @ + 131.93/s (n=1000) Test with: 100 With grep: Element 100 is in array With given-when: Element 100 is in array With grep: timethis 1000: 2 wallclock secs ( 2.13 usr + 0.00 sys = 2.13 CPU) @ + 469.48/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.97 usr + 0.02 sys = 7.99 CPU) @ + 125.16/s (n=1000) Test with: 1000 With grep: Element 1000 is in array With given-when: Element 1000 is in array With grep: timethis 1000: 2 wallclock secs ( 2.29 usr + 0.00 sys = 2.29 CPU) @ + 436.68/s (n=1000) With given-when: timethis 1000: 8 wallclock secs ( 7.69 usr + 0.01 sys = 7.70 CPU) @ + 129.87/s (n=1000) Test with: 10000 With grep: Element 10000 is in array With given-when: Element 10000 is in array With grep: timethis 1000: 2 wallclock secs ( 2.05 usr + 0.00 sys = 2.05 CPU) @ + 487.80/s (n=1000) With given-when: timethis 1000: 10 wallclock secs ( 9.23 usr + 0.01 sys = 9.24 CPU) @ + 108.23/s (n=1000) Test with: 10001 With grep: Element 10001 is not in array With given-when: Element 10001 is not in array With grep: timethis 1000: 2 wallclock secs ( 2.29 usr + 0.00 sys = 2.29 CPU) @ + 436.68/s (n=1000) With given-when: timethis 1000: 9 wallclock secs ( 8.54 usr + 0.01 sys = 8.55 CPU) @ + 116.96/s (n=1000) Test with: abcd With grep: Element abcd is in array With given-when: Element abcd is in array With grep: timethis 1000: 2 wallclock secs ( 2.28 usr + 0.00 sys = 2.28 CPU) @ + 438.60/s (n=1000) With given-when: timethis 1000: 10 wallclock secs ( 9.53 usr + 0.03 sys = 9.56 CPU) @ + 104.60/s (n=1000)

        Bye.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://974933]
Approved by ww
Front-paged by toolic
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2024-04-24 04:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found