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

This might seem simple to alot of you old hash veterans, but I keep hitting this error even though I know it lurks there. I'm often finding myself, using hashes to hold identifiers to objects, especially in Tk, where I want to create a large set of non-anonymous objects in loops. So almost everytime I try to set up my loops, I run into the "Can't use string ("1") as a HASH ref while "strict refs" in use at....." error.

So I've read all the stuff on auto-vivication, and similar posts. What I end up doing is falling back on a little trick I've stumbled upon, and just resort to using it to get around the error. My question is:

Is it correct to think about it this way, or am I just lucky? :-)

My idea is you can't create "depth" in a hash, by assigning a value to it. The code below shows the basic idea.

Usually I have a base item as a hash, like $box{$X}{$Y}{$z}. Now the problem comes when you want to attach data to it, if the "depth" for what you want to assign a value to dosn't exist yet, it gives an error; but if you "artificially" create the depth, even if it's undef, it all works well.

So in the code, I want to add a {'text'} key to the hash, but it won't let me, until I create a fake key {'rect'} at that level, then all goes well. But then my "base" turns from $box{$X}{$Y}{$z} to $box{$X}{$Y}{$z}{'rect'}. This is just an annoyance, but it always seems to work. Does anyone see pitfalls in this approach.?

#!/usr/bin/perl use warnings; use strict; # demonstrates strict hash refs # Commonly seen error # Can't use string ("1") as a HASH ref while "strict refs" in use at . +/z line my %box; my $x = 10; my $y = 10; my $z = 1; foreach my $X ( 0 .. $x ) { foreach my $Y ( 0 .. $y ) { # you cannot create a new hash depth by assigning # a value to it $box{$X}{$Y}{$z} = 1; #dosn't work and causes err +or below # $box{$X}{$Y}{$z}{'rect'} = 1 ; #works # $box{$X}{$Y}{$z}{'rect'} = undef ; #works $box{$X}{$Y}{$z}{'text'} = 2; #this gives strict refs error + } }

I'm not really a human, but I play one on earth. flash japh

Replies are listed 'Best First'.
Re: The "no strict refs" problem revisited.
by Joost (Canon) on May 21, 2004 at 22:21 UTC
    $box{$X}{$Y}{$z} = 1; # DOES work # $box{$X}{$Y}{$z}{'rect'} = 1 ; # 'works' because it's comme +nted out # $box{$X}{$Y}{$z}{'rect'} = undef ; # see above $box{$X}{$Y}{$z}{'text'} = 2; # refs error
    The reason the last line gives an error is that it does the equivalent of
    my $z = 1; $z->{'text'} = 2;
    The 2 lines above it don't work if you take away the # at the begining of the lines.

    A hash of hashes can be automatically created, but perl will not convert a scalar value in it to a hash unless it's undefined. By setting $box{$X}{$Y}{$z} = 1, you prevent $box{$X}{$Y}{$z} from being a hash.

    Personally I always find that if you write this as

    $box{$X}->{$Y}->{$z} = 1; $box{$X}->{$Y}->{$z}->{'text'} = 2;
    it's much more clear what's going on, though it's more typing.

    Update:
    Try doing this instead:

    $box{$X}{$Y}{$z} = undef;
    if you really want to create the 'depth' of that HoH.

    Update2:
    Though you really don't need it:

    $box{$X}{$Y}{$z}{'text'} = 2;
    on it's own (without the preceding 3 lines) works perfectly.

    See also perlref

Re: The "no strict refs" problem revisited.
by jonadab (Parson) on May 22, 2004 at 01:31 UTC
    "Can't use string ("1") as a HASH ref while "strict refs" in use at....." error.

    This sounds to me like something's not quite right in the code -- unless you really wanted to assign a value to a key in the %1 hash. If that's not what you intended, the strictures are helping you, by pointing out that your code doesn't really do what you want.

    $box{$X}{$Y}{$z} =  1;              #dosn't work and causes error below

    What do you mean, "doesn't work"? This code works just fine, if by "work" you mean to assign the value 1 to the scalar value $box{$X}{$Y}{$z} (which could otherwise be written as ${${$box{$X}}{$Y}}{$z} or $box{$X}->{$Y}->{$z} and which is the value for key $z within the hash that is the value for key $Y within the hash that is the value for key $X within %box). What did you think this line would do, if not assign a value of the number 1? That's what it does.

    #      $box{$X}{$Y}{$z}{'rect'} = 1 ;      #works

    I'm not certain this works the way you think it does. You've just assigned a numeric value to $box{$X}{$Y}{$z}, but now you're trying to treat that value (that *numeric* value, the one that you just assigned, the number 1) as a hash (or a hash reference, technically). 1 is not a hash (nor a hash reference), so this won't work -- straightforwardly. It could work as a symbolic reference, but in that case you'd be assigning to $1{rect} and $1{text}, which I suspect is not what you wanted to do.

    If you want $box{$X}{$Y}{$z} to be a hashref, why on earth are you making it a number? A number is not a hash reference. If you want a hashref there, put a hashref there:

    $box{$X}{$Y}{$z} = {}; # Assign anonymous hashref. # Or, alternately... %{$box{$X}{$Y}{$z}} = (); # Assign empty list to hash.

    The undef trick the other poster mentions will also work, but for slightly less straightforward reasons. It should (hopefully) be obvious why assigning an empty hashref will provide you with a hashref in which you can subsequently create values. Also, assigning a hashref, even if it's empty, will cause the value to be defined (since it will contain the hashref you assigned); assigning undef merely causes it to exist (where "exists" means only that the key is in the hash but doesn't imply that a value is defined for it). If you don't understand this paragraph, don't worry about it for now; if you aren't testing for existence, you probably don't really need to know about it.

    I *think* that assigning an empty hashref is what you want to do (though I'm not sure why), but I could be misunderstanding. If so, please explain in more detail what it is you actually want to do, so we can help better.


    ;$;=sub{$/};@;=map{my($a,$b)=($_,$;);$;=sub{$a.$b->()}} split//,".rekcah lreP rehtona tsuJ";$\=$;[-1]->();print
Re: The "no strict refs" problem revisited.
by zentara (Cardinal) on May 22, 2004 at 13:02 UTC
    By setting $box{$X}{$Y}{$z} = 1, you prevent $box{$X}{$Y}{$z} from being a hash.

    Ah, that's the part which eluded. Thanks.


    I'm not really a human, but I play one on earth. flash japh