Search code examples
perlperl-hash

Perl Hash references to the hash values inside same hash


My requirement is as below. Inside the same hash the values of keys are dependent on other key value as shown below

my %test;

$test{map}{a} = 32;
$test{map}{b} = $test{map}{a}+10;
$test{ref}{r} = $test{map}{b};

So, when I do print Dumper(\%test); I get

$VAR1 = {
          'ref' => {
                     'r' => 42
                   },
          'map' => {
                     'a' => 32,
                     'b' => 42
                   }
        };

If I change the hash value

$test{map}{a} = 42

I get

$VAR1 = {
          'ref' => {
                     'r' => 42
                   },
          'map' => {
                     'a' => 42,
                     'b' => 42
                   }
        };

Instead, I should have the updated hash %test as shown below

$VAR1 = {
          'ref' => {
                     'r' => 52
                   },
          'map' => {
                     'a' => 42,
                     'b' => 52
                   }
        };

How to achieve the above result? Any help is much appreciated


Solution

  • The semantics of the code you wrote is not what you imagined. In particular:

    $test{map}{b} = $test{map}{a}+10;
    $test{ref}{r} = $test{map}{b};
    

    These are not -as I think you imagined- "rules" to obtain the value of $test{map}{b} and $test{map}{b} every time someone reads them, but instructions that when executed modify the value associated with the keys b and r. And that's it.

    If you want the elements in your hash to be dynamic, one possible approach could be to use references to subroutines, plus a mechanism to evaluate these rules when the user asks for the values. But be advised that could get complicated: for example, what about circular references? Or rules that reference other rules, as the key r in your example?

    Anyway, here some code as a proof of concept:

    use strict;
    use warnings;
    use v5.10;
    
    my %test;
    
    $test{map}{a} = 32;
    $test{map}{b} = sub { evaluate( $test{map}{a} ) + 10 };
    $test{ref}{r} = sub { evaluate( $test{map}{b} ) };
    
    sub evaluate {
      my $expr = shift;
      if ( ref $expr eq 'CODE' ) {
        # We need to execute the procedure indicated 
        # to obtain a value
        return $expr->();
      }
      else {
        # Otherwise, we just return what we found associated to the key
        return $expr;
      }
    }
    
    say evaluate( $test{ map }{ a } ); # 32
    say evaluate( $test{ map }{ b } ); # 42
    say evaluate( $test{ ref }{ r } ); # 42
    
    $test{map}{a} = 42;
    
    say evaluate( $test{ map }{ a } ); # 42
    say evaluate( $test{ map }{ b } ); # 52
    say evaluate( $test{ ref }{ r } ); # 52
    

    Again, developing a general and solid solution is by no means a trivial project. If you're interested in these techniques from a Perl point of view a very good book is Higher Order Perl, also available online for free.