Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Dynamic Hash value

by Bheema_Tyco (Novice)
on Apr 21, 2010 at 07:31 UTC ( #835979=perlquestion: print w/replies, xml ) Need Help??

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

Hi I am new to Perl

i have initial variable like

my $ini=4; my $last=2;

i am creating hash in the following way

my %result=( SAS1=>$ini-$last, SAS2=>$last-$ini, SAS3=>$ini-$last );

then i am printing the hash values

O/P will be like this

2

-2

2

OK

now i wants to change the value of variable like

my $ini=10; my $last=5;

Now my problem is whenever i change the those variable

then those values should reflect in the hash %result

and whenever i print the hash it should display the latest result like

5

-5

5

can anybody help me :-)

Replies are listed 'Best First'.
Re: Dynamic Hash value
by Corion (Patriarch) on Apr 21, 2010 at 07:44 UTC

    You need to delay the evaluation. Perl calculates each expression as it runs it, so later changes will not change the values already computed:

    #!perl -w use strict; my $ini=4; my $last=2; my %result=( SAS1=>$ini-$last, # 2 SAS2=>$last-$ini, # -2 SAS3=>$ini-$last # 2 );

    If you want to keep changing $ini and $last, you can do that in a subroutine:

    #!perl -w use strict; use Data::Dumper; sub get_SAS { my ($ini,$last) = @_; my %result=( SAS1=>$ini-$last, # 2 SAS2=>$last-$ini, # -2 SAS3=>$ini-$last # 2 ); return %result }; my %res42 = get_SAS(4,2); print Dumper \%res42; my %res105 = get_SAS(10,5); print Dumper \%res105; __END__ $VAR1 = { 'SAS3' => 2, 'SAS2' => -2, 'SAS1' => 2 }; $VAR1 = { 'SAS3' => 5, 'SAS2' => -5, 'SAS1' => 5 };

    There are also other ways to delay the calculation, but I think this is the easiest approach.

Re: Dynamic Hash value
by BrowserUk (Patriarch) on Apr 21, 2010 at 07:54 UTC

    You can achieve this using tie on the hash entries you wish to be dynamic:

    #! perl -slw use strict; use Data::Dump qw[ pp ]; { package Tie::Scalar::Dynamic; sub TIESCALAR { my( $class, $coderef ) = @_; return bless $coderef, $class; } sub FETCH { my $this = shift; return $this->(); } sub STORE { die 'This tied variable is readonly'; } } my( $ini, $last ) = ( 4, 2 ); my %result = ( SAS1 => 0, SAS2 => 0, SAS3 => 0, ); tie $result{ SAS1 }, 'Tie::Scalar::Dynamic', sub{ $ini - $last }; tie $result{ SAS2 }, 'Tie::Scalar::Dynamic', sub{ $last - $ini }; tie $result{ SAS3 }, 'Tie::Scalar::Dynamic', sub{ $ini - $last }; pp \%result; ( $ini, $last ) = ( 123, -456 ); pp \%result; __END__ c:\test>835979.pl { SAS1 => 2, SAS2 => -2, SAS3 => 2 } { SAS1 => 579, SAS2 => -579, SAS3 => 579 }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Dynamic Hash value
by almut (Canon) on Apr 21, 2010 at 08:01 UTC

    Another version, using a tied hash:

    #!/usr/bin/perl package DynHash; use Tie::Hash; our @ISA = 'Tie::StdHash'; sub TIEHASH { my $class = shift; return bless {}, $class; } sub FETCH { my $self = shift; return ${ $self->{$_[0]}[0] } - ${ $self->{$_[0]}[1] } } package main; use Data::Dumper; tie my %result, "DynHash"; my $ini = 4; my $last = 2; %result = ( SAS1 => [ \$ini, \$last ], SAS2 => [ \$last, \$ini ], SAS3 => [ \$ini, \$last ] ); print Dumper \%result; $ini = 10; $last = 5; print Dumper \%result; __END__ $VAR1 = { 'SAS3' => 2, 'SAS2' => -2, 'SAS1' => 2 }; $VAR1 = { 'SAS3' => 5, 'SAS2' => -5, 'SAS1' => 5 };

    i.e., store references to $ini and $last in the hash, and dynamically recompute the difference from them at the time the values of the (tied) hash are being fetched.

Re: Dynamic Hash value
by GrandFather (Saint) on Apr 21, 2010 at 08:04 UTC

    Why?

    Although there are ways to achieve the effect you describe (as others have indicated), it is likely that what you want to do can best be approached in a different way. Corion's sub get_SAS is a good example of how a very slight rethink of the problem may yeld a better solution, but we can't help you best (except by guessing and getting lucky) unless you supply the bigger picture.

    Given that you show the result of printing the values contained in the hash it may be that you don't need a hash at all, but that an array may be more appropriate. How can we tell?

    True laziness is hard work

      Actually Like SAS1, SAS2, SAS3, ...etc are there. Based on those value i am calculating the value say $some_value.

      which($some_value) may either $ini-$last or $last-$ini

      So for that i am building hash %result with SAS1, SAS2., SAS.. as key and either $ini-$last or $last-$ini as value

      So later in the code if i change the values of either $ini or $last then this changed value should reflect in the hash such that whenever i retrieved hash value using SASx as key then it should give the latest result

      .

      If You wants to suggest something like Tie:Scalar please bit elaborate on Tie:Scalar thing as i dont know about that

      Because somebody has suggested that. But i didn't get that. I found Corion's example is good actually

      Please help me

      Thanks

        It is a most unusual way to solve this kind of selection problems by using references. You get into the dreaded Action at A Distance anti-pattern by doing so. It is much more obvious to just write a function:

        my $last = 0; my $ini = 0; ... $last = <something else>; ... $ini = <something else>; ... # now you need $some_value: my $selector = 'SAS1'; my $some_value = compute_sas($selector, $ini, $last); sub compute_sas { my($selector, $ini, $last) = @_; if($selector eq 'SAS1') { return $ini - $last; } else { return $last - $ini; } }

        Note that the sub compute_sas is sloppy, untested and probably not giving the right results, but it should illustrate my point.

        > If You wants to suggest something like Tie:Scalar please bit elaborate on Tie:Scalar thing as i dont know about that

        Because somebody has suggested that. But i didn't get that.

        I suggested others already "elaborated", but it seems you didn't read ...

        Paṛha kr̥payā!

        > I found Corion's example is good actually
        >
        > Please help me

        My helpful advice: Use Corions example!

        > Thanks

        Your welcome! 8)

        Cheers Rolf

Re: Dynamic Hash value
by rovf (Priest) on Apr 21, 2010 at 07:49 UTC
    To really achieve what you want, you would need to give up in syntax a bit. For example, you could store a reference to the variable (instead of its value) into the hash, or a sub (in this case, a closure) which, when called, returns the actual value. You could make the accessing of the values a bit more fancy by using tie on your hash; but your requirement sounds so odd that we have maybe a XY Problem here - so maybe it would help if you would first explain why you want to do this. Then it would be easier to find a nice solution.

    -- 
    Ronald Fischer <ynnor@mm.st>
Re: Dynamic Hash value
by LanX (Sage) on Apr 21, 2010 at 08:42 UTC
    Maybe all you need are objects?

    (pure intellectual masturbation ;)

    $\="\n";$,="\t"; { package SAS; our ($ini,$last); sub _1 {$ini-$last} sub _2{$last-$ini} sub _3 {$ini-$last} } $SAS::ini=4; $SAS::last=2; print &SAS::_1, &SAS::_2 , &SAS::_3;
    #or

    package SAS2; our ($ini,$last); sub _1 {$ini-$last} sub _2{$last-$ini} sub _3 {$ini-$last} package main; $ini=10; $last=5; print SAS2::_1(), SAS2::_2(), SAS2::_3();

    If you really need a hash like structure you may wanna try investigate the %SAS:: hash to grep all functions starting with an underscore.

    print map {$_->()} grep {/::_/} values %SAS::;

    of course this map-grep operation could be a method of your object ...

    Cheers Rolf

    UPDATE: expanded code examples

Re: Dynamic Hash value
by LanX (Sage) on Apr 21, 2010 at 07:52 UTC
    Yes it's possible to automatically propagate any change of your scalars to the hash using Tie::Scalar.

    But this technique is certainly not appropriate for a beginner.

    Cheers Rolf

    ) or Tie::Hash like others mentioned, that depends on if you wanna push or pull the changes.

Re: Dynamic Hash value
by arc_of_descent (Hermit) on Apr 21, 2010 at 07:48 UTC
    You can create scalar references to $ini and $last, and use these references in your %results hash. Then when you change the values of $ini and $last, the hash will refer to the changed values. Read up on references here - perlref
    my $ini = 5; my $ini_ref = \$ini; print $$ini_ref; # outputs 5 $ini = 6; print $$ini_ref; outputs 6
      no in this case it's not that simple because the scalars are not only referenced but also part of a calculation.

      Cheers Rolf

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://835979]
Approved by rovf
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (3)
As of 2023-06-05 14:16 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you go to conferences?






    Results (25 votes). Check out past polls.

    Notices?