http://www.perlmonks.org?node_id=130850

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

Monks ~

Two questions here:

  1. Why doesn't this work like I expect ("false expectations" is my best answer, but it isn't helping me),
  2. Is there a better way to do what I'm attempting?

I want to format and display a heading iff that heading's children contain data.

#!/usr/bin/perl -T use strict; use warnings; # prints nothing, as expected print do_head( "Empty" ); # prints "<H4>Appearance</H4>"; wish it didn't my @p{ 'eyes', 'hair', 'etc' } = (); print do_head( "Appearance", @p{ 'eyes', 'hair', 'etc' }); #------------------------------------------------ sub do_head { my $heading = shift; my @data = @_; return "<H4>$heading</H4>" if @data; }

I expected the do_head sub to put an empty list into @data; said list would then evaluate as false in a scalar context. This doesn't work, of course, which is why I'm here. I'm sure this is based on my faulty understanding of arrays and hashes. I've re-read the Camel on this subject and not attained enlightenment. Help?

Thanks
--
man with no legs, inc.

Replies are listed 'Best First'.
Re: Testing array of hash values
by athomason (Curate) on Dec 11, 2001 at 07:52 UTC
    Like dws, I'm surprised this would run; it doesn't for me under 5.6.1. Your declaration of %p almost has the syntax for a hash slice, except that you can't do that in a my declaration. Something like my %p; @p{ 'eyes', 'hair', 'etc' } = (); would be legitimate, if unusual.

    As for why do_head doesn't do what you think, I believe you're misunderstanding the behavior of undef in an array. undef in an array or hash doesn't just disappear, it's still something. This is notably different from its behavior in a list (which is not an array!). For instance, see the behavior of this snippet:

    @a = ( ); @c = ( undef ); die "a" if @a; die "b" if ( undef, undef ); die "c" if @c

    Particularly, it dies with 'c', not 'a' or 'b'. In a boolean context--like an if statement--an array takes the value of its length, not its members. In my example, this is 1, which is a true value. Likewise, your three undefs give @data a true value. So, your list of undefs is in fact true when assigned to an array. You were probably expecting the behavior of a list, which in a boolean context evaluates to its last element (e.g., die "d" if ( 1, undef ); die "e" if ( undef, 1 ); dies with 'e'). The solution, if you wish to keep the behavior of printing when the elements are defined, can be found in dws' post.

Re: Testing array of hash values
by dws (Chancellor) on Dec 11, 2001 at 07:26 UTC
    It looks like this is intended to be a CGI, and yet it doesn't emit the correct response header, so I'll assume this is just a code fragment.

    What, exactly, do you expect   @p{'eyes', 'hair', 'etc'} =(); to do? It rather looks like a syntax error to me. If you're trying to pre-load a hash with keys, you might try

    my %p; $p{$_} = undef for qw(eyes hair etc);
    But that begs the question of what your do_print is trying to do, since   @p{ 'eyes', 'hair', 'etc' } results in a three element array, with each element being undef. Assuming that you're trying to return the heading if and only if any one of the elements is present, you could do
    sub do_head { my($heading, @data) = @_; return "<h4>$heading</h4>" if grep { defined($_) } @data; return ""; }

      He may have been trying for:

      my %p; @p{ qw( eyes hair etc ) } = ( undef ) x 3;

      Of course that's crufty in that you have to hard code in the number of elements.

        Actually,
        my %p; @p{'eyes', 'hair', 'etc'} = ();
        is a common idiom for using a hash slice to load a hash with keys and undef values.
      Yes, it's a fragment, but it runs as it should from the command line. The line
      @p{'eyes', 'hair', 'etc'} =();
      is just to show that those keys are empty. It was indeed the undef that was getting me. Your code works. Thanks.
      --
      man with no legs, inc.