http://qs321.pair.com?node_id=430556

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

I didn't quite know how to label this one, but here's what i want to do..

Lets says i have numeric variables ranging from $Sel1Ttl all the way through to $Sel80Ttl,
is there an easy method to add all the values together via a loop.
Instead of typing $Sel1Ttl + $Sel2Ttl + Sel3Ttl...... + $Sel80Ttl.

I.e. can you break the name of the scalar up and insert a number into it $Sel1......80Ttl so i can then use that in my additions.. ?

Replies are listed 'Best First'.
Re: Adding scalars but with a twist..
by Corion (Patriarch) on Feb 13, 2005 at 09:44 UTC

    Put them in an array instead and use that. This is one of the reasons why everybody told you in your previous post that it's not a good thing to want to get at a variable through a second variable by its name.

      Ok i'd love to, except there's about 500,000 lines of code previously written by someone else. (badly). Trust me this is not the way i'd normal write things but am forced to..
      Unfortunately whoever wrote this previously was terrible and it's just easier to keep running along thier lines, the reasons i'm asking the questions is i would never write a program like this but have to as small additions are being made.

      I.e. everywhere through the code it has $Sel1Ttl + $Sel2Ttl......$Sel80Ttl already written..

      Now everyone in thier answers has concentrated on the addition of the values, where the real problem is the $SelMaking this number changeTtl part...

        I guess some heavy refactoring is needed then. Should not be that hard to search&replace all the /\$Sel(\d+)Ttl\b/ with "$SelTtl\[$1]". Of course you should then go and replace those insane $Sel1Ttl + $Sel2Ttl......$Sel80Ttl, by sum(@SetTtl) (from List::Util), but that's something that can wait if necessary.

        Jenda
        We'd like to help you learn to help yourself
        Look around you, all you see are sympathetic eyes
        Stroll around the grounds until you feel at home
           -- P. Simon in Mrs. Robinson

        except there's about 500,000 lines of code
        In a single script? Assuming an average length of 40 Bytes per line that would be 19 MB!


        holli, /regexed monk/
Re: Adding scalars but with a twist..
by holli (Abbot) on Feb 13, 2005 at 09:53 UTC
    This can easily be done using eval(), even under strict:
    use strict; my $a1 = 3; my $a2 = 4; my $a3 = 5; my $sum = 0; for ( 1..3 ) { eval "\$sum += \$a$_"; } print $sum;
    p.s.
    i personally would prefer to rewrite the script you have to update (as stated in previous post), because not doing so is false lazyness in my eyes.
    holli, /regexed monk/
Re: Adding scalars but with a twist..
by Joost (Canon) on Feb 13, 2005 at 11:57 UTC
    I.e. can you break the name of the scalar up and insert a number into it $Sel[1......80]Ttl so i can then use that in my additions.. ?
    Yes, but the best way to do almost that it is to use an array:
    for my $number (1 .. 80) { $selTtl[$number] = $value; }
    Really, the advantages of not having to type 2 more characters ([]) are not worth it, considering the potential problems.

Re: Adding scalars but with a twist..
by TilRMan (Friar) on Feb 13, 2005 at 18:38 UTC

    If you want to go a step farther than eval, you can use some tie magic to "shadow" the original $Sel1Ttl variables with a real array.

    my ( $a1z, $a2z, $a3z ) = qw( 42 13 666 ); my @az; tie @az, 'FixedArrayOfScalarRefs', "There is no element zero!", # Dummy element 0 \( $a1z, $a2z, $a3z ); # Elements 1 .. 3 for ( 1 .. 3 ) { print "\$a${_}z = $az[$_]\n"; }

    Since the dummy element zero is not a reference, the tied array will croak if you accidentally use it:

    print "\$a0z = $az[0]\n"; # Array index 0 out-of-bounds at bar.pl line 63

    With the array, you can use the traditional for (1 .. 3) { $sum += $az[$_] } or the IMHO prettier:

    use List::Util qw( sum ); my $sum = sum( @az[ 1 .. 3 ] ); print "sum = $sum\n";

    If you'd prefer $Sel1Ttl = $SelTtl[0], leave out the dummy element in the tie. If you do, the sum becomes:

    my $sum = sum( @az );
Re: Adding scalars but with a twist..
by davido (Cardinal) on Feb 14, 2005 at 03:15 UTC

    Remember how in your previous thread I mentioned that the global symbol table is, itself, a hash? So here's the strategy: grep the global symbol table's keys for names that match the pattern " /Sel\d+Ttl/". That's all the variables you're looking for. Next, iterate through those 'keys', dereferencing the global symbol table to get at the values held in those key names (variable names). Here's an example in action. Remember, I warned you previously this is a road to perdition. I'll see you in hell. ;) (just teasing)

    no strict qw/vars/; ( $sel1, $sel2, $sel3, $sel4, $sel5 ) = ( 1, 2, 3, 4, 5 ); my $sum = 0; $sum += ${$::{$_}} foreach grep /sel\d+/, keys %::; print $sum, "\n";

    Magic!

    Update: I realize that you could just dereference the individual keynames, as in  $$_, within the foreach loop, but I left the use of the global symbol hash in the code so that future maintainers would know without a doubt that there is something going on here that needs further investigation to understand. $$_ isn't obvious enough to cry out, "I'm a symbolic ref! Beware!"


    Dave

Re: Adding scalars but with a twist..
by Dietz (Curate) on Feb 13, 2005 at 09:51 UTC
    #!/usr/bin/perl use strict; use warnings; my $Sel1Ttl = 1; my $Sel2Ttl = 2; my $Sel3Ttl = 3; my $result; for ($Sel1Ttl..$Sel3Ttl) { $result += $_; } print "$result\n"; # gives 6

    Update:
    Sorry, this is just interpolating the variable values and using it for a range, thus it is giving nonsense if there are different values
Re: Adding scalars but with a twist..
by Anonymous Monk on Feb 14, 2005 at 10:05 UTC
    If they are package variables, you can use symbolic references:
    my $sum = 0; foreach my $i (1 .. 80) { no strict 'refs'; $sum += ${"Sel${i}Ttl"}; }
    If not, you can use 'eval':
    my $sum = 0; foreach my $i (1 .. 80) { $sum += do {eval "$Sel${i}Ttl"} }
    Or if you want to get fancy:
    my $sum = 0; local $" = "+"; eval "\$sum = @{[map {qq {\$Sel${_}Ttl}} 1 .. 80]}";
    All code untested.