fluxrider
fluxrider

Reputation: 53

How to increment through the values in a list?

I have repeatedly tried to search for this question, but cannot form a search that produces results that are actually relevant to my question.
I am trying to build a script that parses an SVG file (XML text format file that produces graphic content) looking for specific cues and assigns RGB values. (Each RGB value will be slightly different.)
To do this, I picture multiple incrementing variables (for instance $i, $j & $k) that increment based on triggers found while parsing the text file. However, the amounts that I need to increment are not "1". Also, the values needed are hexadecimal in form. I could set up something where the vars are incremented by a given amount, such as +33, but I would also need to convert numbers to hex, figure out how to start over, etc.

A far more versatile, powerful and elegant approach occurs to me, but I don't know how to go about it.
How can I set up these incrementing variables to increment through the values I've set up in an array?
For example, say my RGB potential values are @rgbval = (00,33,66,99,cc,ff).
How can I make $i go from one value in this list to the next?
Even better, how could I make $i++ (or something similar) mean "go to the next element's value in @rgbval"?
And assuming this is possible, how would I tell Perl to start over at element [0] after reaching the end of the array?

Upvotes: 2

Views: 246

Answers (2)

fluxrider
fluxrider

Reputation: 53

Ikegami, what an excellent bunch of information your response held.
I found three of your proposals too appealing to ignore, and tried to understand them. Your first section was about processing the math described, converting into and out of hex.
I tried to wrestle these steps into a "test of concept" script, along with your suggestion of a modulo reset. (Okay, a "test of understanding of concept".)
For the test script I used an iterator rather than searching for a triggering event, that seemed simpler.
The goal was to have the values increment through the hex numbers listed in the example array, and to start over after the last value. So I iterated up to ten times, to give the values a chance to start over.
After I figured out that I needed to add 51 each time instead of 33 to get those example values, and also had to make the numerical value of my array 51 times larger since I was incrementing by 51, it worked pretty well:

my $num = hex("00");
my @rgbval = qw(a b c d e f);
for my $i (0..10) {
   print ( "For i=$i, \$num is " , sprintf("%02x ", $num) , "\n");
   $num = ( $num + 51 ) % ( 51 * @rgbval );
}

output:
~\Perlscripts>iterate2.pl
For i=0, $num is 00
For i=1, $num is 33
For i=2, $num is 66
For i=3, $num is 99
For i=4, $num is cc
For i=5, $num is ff
For i=6, $num is 00
For i=7, $num is 33
For i=8, $num is 66
For i=9, $num is 99
For i=10, $num is cc

As far as the non-mathy approach, incrementing through the strings of the array, I understood you to be saying I would need to increment the indices that reference the array values. I did manage to confuse my self with the different iterators and what they were doing, but after a few stumbles, I was able to make this approach work as well:

my @hue = qw(00 33 66 99 cc ff);
my $v = 0;
for my $i (0..10) {
   print "\$i=$i, \$hue[$v]=" , $hue[$v] , "\n";
   $v = ( $v + 1 ) % @hue;
}

output:
~\Perlscripts>iterate2.pl
$i=0, $hue[0]=00
$i=1, $hue[1]=33
$i=2, $hue[2]=66
$i=3, $hue[3]=99
$i=4, $hue[4]=cc
$i=5, $hue[5]=ff
$i=6, $hue[0]=00
$i=7, $hue[1]=33
$i=8, $hue[2]=66
$i=9, $hue[3]=99
$i=10, $hue[4]=cc

Your last proposed solution, rotating the array with push and shift seemed perhaps the most novel approach and quite compelling, especially once I realized that if i have a variable that stores the shifted value, that will be the correct value to push next time, around and around. In this approach I don't even have to worry about starting over after the last value; the changing array takes care of that automatically for me:

my @hue = qw(00 33 66 99 cc ff); 
for my $i (0..10) {
   my $curval = shift(@hue);
   print "\$i=$i, \$curval is $curval               \.\.\.And the array is currently: ( @hue )\n";
   push(@hue,$curval);
}

output:
~\Perlscripts>iterate2.pl
$i=0, $curval is 00 ...And the array is currently: ( 33 66 99 cc ff )
$i=1, $curval is 33 ...And the array is currently: ( 66 99 cc ff 00 )
$i=2, $curval is 66 ...And the array is currently: ( 99 cc ff 00 33 )
$i=3, $curval is 99 ...And the array is currently: ( cc ff 00 33 66 )
$i=4, $curval is cc ...And the array is currently: ( ff 00 33 66 99 )
$i=5, $curval is ff ...And the array is currently: ( 00 33 66 99 cc )
$i=6, $curval is 00 ...And the array is currently: ( 33 66 99 cc ff )
$i=7, $curval is 33 ...And the array is currently: ( 66 99 cc ff 00 )
$i=8, $curval is 66 ...And the array is currently: ( 99 cc ff 00 33 )
$i=9, $curval is 99 ...And the array is currently: ( cc ff 00 33 66 )
$i=10, $curval is cc ...And the array is currently: ( ff 00 33 66 99 )

Most educational and helpful! Thanks so much.

Upvotes: 1

ikegami
ikegami

Reputation: 386331

So you have a string that's the hex representation of a number

my $hex = 'cc';

To do arithmetic on it, you first need to convert that to a number.

my $num = hex($hex);

Now we can do arithmetic on it.

$num += 33;

If we want to convert it back to hex, we can use

$hex = sprintf("%02x", $num);

$i is usually used for indexes. If that's what you want, you can use the following:

for my $i (0..$#rgbval) {
   # Do something with $i and/or $rgbval[$i]...
}

If instead you want $i to take on each value, you can use the following:

for my $i (@rgbval) {
   # Do something with $i...
}

But it seems to be you want a counter that wraps around.

The straightforward solution would be use an if statement.

my $i = 0;
while (...) {
   # Do something with $i and/or $rgbval[$i]...

   ++$i;
   $i = 0 if $i == @rgbval;
}

But I'd use modulo arithmetic.

my $i = 0;
while (...) {
   # Do something with $i and/or $rgbval[$i]...

   $i = ( $i + 1 ) % @rgbval;
}

Alternatively, you could rotate the array.

while (...) {
   # Do something with $rgbval[0]...

    push @rgbval, shift(@rgbval);
}

Upvotes: 3

Related Questions