开发者

Perl: Define hash with reference to the same hash, $this->{key}?

How would I create开发者_高级运维 a hash like the follow:

my %hash = (key1=>"Something", key2=>$hash{key1} . "Else");

Can this not be done while when I declare the hash? So far, the only thing I came up with was:

my %hash = (key1=>"Something");
$hash{key2} = $hash{key1} . "Else";


Why not use an intermediate variable?

my $common_value = 'Something';
my %some_hash = ( k1 => $common_value, k2 => $common_value.'Else' );

Update

kemp asked why we need an intermediate variable. I can interpret this question it two ways, I'll endeavor to answer both interpretations.

  1. Why can't you do my %hash = ( k1 => 'foo', k2 => $hash{k1}.'bar' );?

    The right-hand side (rhs) of the expression is evaluated before the left-hand side (lhs). So, when the value for k2 is determine, no assigment to %hash has yet occurred. In fact, %hash has not even been created and entered into the scratch pad yet. (%hash is a lexical variable here, so it won't go into the symbol table.)

  2. Why use a variable rather than another method?

    Well, there are many ways to initialize a hash with values like this. I would use an intermediate variable if I had a small number of related keys to initialize.

    my $cv1 = sub_result() * 5;
    my $cv2 = "Number: $cv1";
    
    my %h = (
        k1 => $cv1,  
        k2 => $cv2,
        k3 => "Numero: $cv1",
        k4 => "Big $cv2",
        k5 => "Round $cv2",
        k6 => "Whole $cv2",
        k7 => "-$cv1",
    );
    

    However, if I had to build up many complicated values that depended on many different keys, I'd probably use a data driven approach to initialize the hash. The exact implementation would depend on details I don't have, but it might look something like this:

    use Scalar::Util qw(reftype);
    
    my @init = ( 
        [ key1 => $value                 ],
        [ key2 => \&make_a_value, 'key1' ],
        [ key3 => \&another_way,  'key2' ],
    );
    
    my %h; 
    for my $spec ( @init ) {
        my $key   = shift @$spec;
        my $value = shift @$spec;
        my @args  = @$spec;
    
        if( reftype $value eq reftype sub {} ) {
            $value = $value->( @h{ @args } );
        }
    
        $h{$key} = $value;
    }
    

    For any case where this question arrises, the choice as to what technique to use depends on many details that are special to that instance. So we are left with the necessity to make generic statements. Intermediate variables are one good way approach the OP's problem. Whether they should be used in his particular case, I can not say.


It cannot be done for 2 reasons:

  1. At the time Perl processes key2 => $hash{key1}, it has not yet associated the memory allocated with key 1 with the hash name hash. In other words, $hash{key1} does not point to the correct place in memory until after key2 => $hash{key1} is already assigned.

  2. Previous reason didn't state the additional problem: you didn't yet place %hash into a symbol table by the time you process key2 => $hash{key1} (again the order of parsing/evaluation), so the compiler will barf if you use strict; as you always should. This one is easily fixable with declaring my %hash in prior statement, but #1 is not.


At the point where you are defining the hash, $hash{key1} does not exist, so you can't do this in one statement. You can do your own solution and use key key1 after you have defined it.


What's wrong with

my %hash = ( key1 => "Something", key2 => "Something" . "Else" );

?

Yes, I know those are placeholders, but whatever they may be you know them at the time you build the hash (or you wouldn't build it at all), so why don't you just join them together?


Firstly, I would just do this with the intermediate value. But, you have two separate more advanced options. I don't suggest /either/ of these if you're new to perl - in fact I rather like the intermediary value solution the best, but for completeness:

  • You can use Tie::Hash which allows you to make an object accessible with hash syntax and hash style dereferencing (->{key1}), you can find more information on this method with perldoc perltie, and the man page linked above for Tie::Hash;
  • You can abandon the idea of using a hash; and, use an object, or even Moose which will allow you to initialize the object from a hash, sharing the same value. I don't see much purpose in a real world example in doing this.

Here is a Moose example.

package Class;
use Moose;
has [qw/key1 key2/] => ( isa => 'Str', is => 'rw', init_arg => 'key1' );

my $o = Class->new( { key1 => 'foobar' } );
print $o->key1, $o->key2;


You can get fairly close, but I'm not sure it has value other than novelty:

$_->{key2} = $_->{key1} . "Else" for my $hash = { key1 => "Something" };


If creating the intermediate variable outside of %hash is off putting, then using a do block may make it more aesthetically pleasing:

my %hash = (
    do {
        my $s = 'Something';
        ( key1 => $s, key2 => $s . 'Else' );
    },
);

/I3az/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜