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

Fellow monks! Is there a better way to test all elements in a list are hashes than the following?
my @a = ({},{},{},{}); my @b = ({},0,{},0); my @c = (0,{},0,{}); my @d = ({}); my @e = (); foreach my $x (\@a, \@b, \@c, \@d, \@e) { print @$x == grep({ref($_) eq 'HASH'} @$x),"\n"; }
The hashes aren't necessarily the same. Also is there an elegant way to avoid the empty list from returning true.

Replies are listed 'Best First'.
Re: How to test all elements in a list are hashes?
by grinder (Bishop) on Jan 17, 2005 at 14:07 UTC
    is there an elegant way to avoid the empty list from returning true

    Well, elegant, I don't know; beauty is in the eye of beholder. If you don't want the empty list to return true, then test for it:

    @$x and @$x == grep { ref $_ eq 'HASH' } @$x

    - another intruder with the mooring in the heart of the Perl

Re: How to test all elements in a list are hashes?
by Random_Walk (Prior) on Jan 17, 2005 at 14:26 UTC

    I think the way you are going is pretty elegant, here is a version altered to return false(0) on an empty array

    perl -le'@a=(a, {}, {});print (( @a==grep {ref $_ eq "HASH"} @a ) ? @a ? 1 : 0 : 0)'

    Cheers,
    R.

    Pereant, qui ante nos nostra dixerunt!
Re: How to test all elements in a list are hashes?
by bsdz (Friar) on Jan 17, 2005 at 14:36 UTC
    Okay, testing for an empty list is probably the most elegant way to do it. But what about the actual test, is it better to use say: -
    print @$x && !grep({ref($_) ne 'HASH'} @$x),"\n";
    over
    print @$x && @$x == grep({ref($_) eq 'HASH'} @$x),"\n";
    ?

      Looks like the double negative way is the fastest except for the empty set. In all other cases the ones comparing the array to the grep'd version may have a lot of comparisons to do before they discover inequality.

      #!/usr/bin/perl -l use strict; use warnings; use Benchmark; my (@a, @b, @c, @d, @e); @a=({}, {}, {}, {}, {}, {}, {}, {}, {}, {}); push @b, @a for (1..10000); push @c, @b, "a"; push @d, "a", @b; @e=(); sub trinary { my $a=shift; print ((@$a == grep {ref $_ eq "HASH"} @$a) ? @$a ? 1 : 0 : 0) } sub double_neg { my $a=shift; print @$a && !grep({ref($_) ne 'HASH'} @$a); } sub equality { my $a=shift; print @$a && @$a == grep({ref($_) eq 'HASH'} @$a); } timethese(9000000, { 'a trinary' => trinary(\@a), 'a double_neg' => double_neg(\@a), 'a equality' => equality(\@a), 'b trinary' => trinary(\@b), 'b double_neg' => double_neg(\@b), 'b equality' => equality(\@b), 'c trinary' => trinary(\@c), 'c double_neg' => double_neg(\@c), 'c equality' => equality(\@c), 'd trinary' => trinary(\@d), 'd double_neg' => double_neg(\@d), 'd equality' => equality(\@d), 'e trinary' => trinary(\@e), 'e double_neg' => double_neg(\@e), 'e equality' => equality(\@e), }); __DATA__ a double_neg: -1 wallclock secs ( 0.47 usr + 0.09 sys = 0.56 CPU) @ +16071428.57/s (n=9000000) a equality: -1 wallclock secs ( 0.62 usr + -0.01 sys = 0.61 CPU) @ +14754098.36/s (n=9000000) a trinary: 4 wallclock secs ( 0.81 usr + 0.05 sys = 0.86 CPU) @ +10465116.28/s (n=9000000) b double_neg: 3 wallclock secs ( 0.72 usr + 0.00 sys = 0.72 CPU) @ +12500000.00/s (n=9000000) b equality: 0 wallclock secs ( 0.75 usr + -0.07 sys = 0.68 CPU) @ +13235294.12/s (n=9000000) b trinary: 6 wallclock secs ( 0.77 usr + 0.00 sys = 0.77 CPU) @ +11688311.69/s (n=9000000) c double_neg: -2 wallclock secs ( 0.52 usr + -0.02 sys = 0.50 CPU) @ +18000000.00/s (n=9000000) c equality: 3 wallclock secs ( 0.61 usr + 0.07 sys = 0.68 CPU) @ +13235294.12/s (n=9000000) c trinary: 0 wallclock secs ( 0.54 usr + 0.00 sys = 0.54 CPU) @ +16666666.67/s (n=9000000) d double_neg: -4 wallclock secs ( 0.29 usr + 0.00 sys = 0.29 CPU) @ +31034482.76/s (n=9000000) (warning: too few iterations for a reliable count) d equality: 0 wallclock secs ( 0.69 usr + 0.00 sys = 0.69 CPU) @ +13043478.26/s (n=9000000) d trinary: 2 wallclock secs ( 0.56 usr + -0.05 sys = 0.51 CPU) @ +17647058.82/s (n=9000000) e double_neg: 7 wallclock secs ( 0.77 usr + 0.06 sys = 0.83 CPU) @ +10843373.49/s (n=9000000) e equality: 1 wallclock secs ( 0.62 usr + 0.00 sys = 0.62 CPU) @ +14516129.03/s (n=9000000) e trinary: 1 wallclock secs ( 0.53 usr + 0.06 sys = 0.59 CPU) @ +15254237.29/s (n=9000000)

      Cheers,
      R.

      Pereant, qui ante nos nostra dixerunt!
        Brilliant - just what I was looking for! If I discover any alternative idioms I shall use Benchmark.pm to test them.
Re: How to test all elements in a list are hashes?
by ysth (Canon) on Jan 17, 2005 at 22:08 UTC
    "are hashes" is not a precise problem description. What about
    my @a = bless([], "HASH");
    ? What about:
    {package HASH; use overload "%{}"=>sub{$_[0][0]}} my @a = bless([{}], "HASH");
    ? The broadest way to tell if something is a hashref is...to see if it's a hashref:
    print @$x == grep({eval {\%$_}} @$x),"\n";
    All untested.
      Well if you're going to bless bananas into apples and overload the task of peeling them, what do you expect! But on the otherhand if you're writing a module and generating and testing your own 'hashes' I don't expect it's a problem. In any case thank you ysth! :-)