Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

How to declare variables per loop

by Anonymous Monk
on Oct 11, 2011 at 13:46 UTC ( [id://930814]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Monks,

I need to declare a lot of variables in the following way:
my $abc_this_01 = "C:/Stuff/abc_this_01.csv"; my $def_this_01 = "C:/Stuff/def_this_01.csv"; my $ghi_this_01 = "C:/Stuff/ghi_this_01.csv"; my $jkl_this_01 = "C:/Stuff/jkl_this_01.csv"; my $mno_this_01 = "C:/Stuff/mno_this_01.csv"; my $abc_this_02 = "C:/Stuff/abc_this_02.csv"; my $def_this_02 = "C:/Stuff/def_this_02.csv"; my $ghi_this_02 = "C:/Stuff/ghi_this_02.csv"; my $jkl_this_02 = "C:/Stuff/jkl_this_02.csv"; my $mno_this_02 = "C:/Stuff/mno_this_02.csv"; # ... my $abc_that_01 = "C:/Stuff/abc_that_01.csv"; my $def_that_01 = "C:/Stuff/def_that_01.csv"; my $ghi_that_01 = "C:/Stuff/ghi_that_01.csv"; my $jkl_that_01 = "C:/Stuff/jkl_that_01.csv"; my $mno_that_01 = "C:/Stuff/mno_that_01.csv"; # etc.
I thought if it were possible to initialize the variables per loop. My knowledge in Perl however is definitely not good enough to manage this.
I tried it like this:
use strict; use warnings; my @component = qw / abs def gki jkl mno /; for (@component) { my $comp = $_; our ${$comp.'_this_01'} = "C:/Stuff/${_}_this_01.csv"; }
but it seems to be fundamentally wrong.
Could you give me an advice?
Thanks in advance.
VE

Replies are listed 'Best First'.
Re: How to declare variables per loop
by aartist (Pilgrim) on Oct 11, 2011 at 13:57 UTC
    You might want to use 'hash' for this purpose

    my $hash; my @component = qw / abs def gki jkl mno /; my $root_dir = 'C:/Stuff/'; foreach my $c (@component){ foreach my $t (qw(this that)){ foreach my $n ('01','02'){ my $csv_file = join '_' => ($c,$t,$n); $hash->{$c}{$t}{$n} = $root_dir.$csv_file; } } }
    Or depend upon your needs, you can simplify the code this way. That way, you will not need to pre-populate the hash.
    my $root_dir = 'C:/Stuff/'; my $component = 'abs'; my $this_that = 'this'; my $index = '01'; my $csv_file = (join '_' => ($component,$this_that,$index)). '.csv'; my $csv_file_path = $root_dir.$csv_file;
      Thank you very much, I will try that.
      VE
Re: How to declare variables per loop
by dreadpiratepeter (Priest) on Oct 11, 2011 at 13:58 UTC
    This has XY Problem written all over it. Try explaining to us in more broad terms what you are trying to accomplish that you think you need all these variables. I bet there is a far more elegant solution.


    -pete
    "Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere."
      Thank you very much!
      I compose commands for gnuplot in the following way (it is all a single line):
      plot $years $yscala "$abc_this_01" title "" with errorbars lt rgb "$co +lor_abc" lw 2, "$abc_this_01" title "{/Helvetica=10 ABC_Stuff}" with +lines lt rgb "$color_abc" lw 3,"$def_this_01" title "" with errorbars + lt rgb "$color_def" lw 2, "$def_this_01" title "{/Helvetica=10 DEF_S +tuff}" with lines lt rgb "$color_def" lw 3, "$ghi_this_01" title "" w +ith errorbars lt rgb "$color_ghi" lw 2, "$ghi_this_01" title "{/Helve +tica=10 GHI_Stuff}" with lines lt rgb "$color_ghi" lw 3, "$jkl_this_0 +1" title "" with errorbars lt rgb "$color_jkl" lw 2, "$jkl_this_01" t +itle "{/Helvetica=10 JKL_Stuff}" with lines lt rgb "$color_jkl" lw 5, + "$mno_this_01" with steps lt 1 lw 5 title "{/Helvetica=10 Abnormalit +y Border}"
      and the files looks like this:
      # Value Low High 2006 78.6 76.5 80.6 2007 86.3 84.7 87.6 2008 92.7 92.3 93.1 2009 95.3 94.9 95.6 2010 96.8 96.5 97.1 2011 97.0 96.5 97.5
      It plots diagramms with the values (plus y-errorbars) from four sources (abc, def ...) and with the abnormality border. There are ca. 20 topics ("this", "that" etc.) with up to 8 subgroups (01, 02 etc.).
      I have indeed the feeling that the composition is somehow ugly. Leastwise it works :-)
      However I would be very glad for any suggestion how I could organize the data better.
      Thanks again.
      VE
        Blowing some white space into your command shows that there is a pattern that we might take advantage of.
        plot $years $yscala "$abc_this_01" title "" with errorbars lt rgb "$color_abc" lw 2, "$abc_this_01" title "{/Helvetica=10 ABC_Stuff}" with lines lt rgb "$c +olor_abc" lw 3, "$def_this_01" title "" with errorbars lt rgb "$color_def" lw 2, "$def_this_01" title "{/Helvetica=10 DEF_Stuff}" with lines lt rgb "$c +olor_def" lw 3, "$ghi_this_01" title "" with errorbars lt rgb "$color_ghi" lw 2, "$ghi_this_01" title "{/Helvetica=10 GHI_Stuff}" with lines lt rgb "$c +olor_ghi" lw 3, "$jkl_this_01" title "" with errorbars lt rgb "$color_jkl" lw 2, "$jkl_this_01" title "{/Helvetica=10 JKL_Stuff}" with lines lt rgb "$c +olor_jkl" lw 5, "$mno_this_01" with steps lt 1 lw 5 title "{/Helvetica=10 Abnormality +Border}"
        We could consider putting the data into a hash and then feed that into a template. I've used sprintf here but there are many alternatives. I've made some guesses about the actual spec.
        #! /usr/perl/bin use warnings; use strict; my %args = ( years => 2011, yscala => q{xxx}, sources => [ { source => q{abc}, color => q{red}, }, { source => q{def}, color => q{blue}, }, { source => q{ghi}, color => q{yellow}, }, { source => q{jkl}, color => q{green}, }, ], topic => q{this}, number => q{01}, abnormality_border => q{mno}, ); my $cmd = build_command(%args); print $cmd; sub build_command { my %args = @_; my $cmd = sprintf( qq{plot %s %s\n\n}, $args{years}, $args{yscala} ); for my $source (@{$args{sources}}){ $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" title "" with errorbars lt rgb "%s_%s +" lw 2,\n}, $source->{source}, $args{topic}, $args{number}, $source->{color}, $source->{source}, ); $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" title "{/Helvetica=10 ABC_Stuff}" wit +h lines lt rgb "%s_%s" lw 3,\n\n}, $source->{source}, $args{topic}, $args{number}, $source->{color}, $source->{source}, ); } $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" with steps lt 1 lw 5 title "{/Helvetica +=10 Abnormality Border}"}, $args{abnormality_border}, $args{topic}, $args{number}, ); return $cmd; }
        output
        plot 2011 xxx "C:/Stuff/abc_this_01.csv" title "" with errorbars lt rgb "red_abc" lw + 2, "C:/Stuff/abc_this_01.csv" title "{/Helvetica=10 ABC_Stuff}" with line +s lt rgb "red_abc" lw 3, "C:/Stuff/def_this_01.csv" title "" with errorbars lt rgb "blue_def" l +w 2, "C:/Stuff/def_this_01.csv" title "{/Helvetica=10 ABC_Stuff}" with line +s lt rgb "blue_def" lw 3, "C:/Stuff/ghi_this_01.csv" title "" with errorbars lt rgb "yellow_ghi" + lw 2, "C:/Stuff/ghi_this_01.csv" title "{/Helvetica=10 ABC_Stuff}" with line +s lt rgb "yellow_ghi" lw 3, "C:/Stuff/jkl_this_01.csv" title "" with errorbars lt rgb "green_jkl" +lw 2, "C:/Stuff/jkl_this_01.csv" title "{/Helvetica=10 ABC_Stuff}" with line +s lt rgb "green_jkl" lw 3, "C:/Stuff/mno_this_01.csv" with steps lt 1 lw 5 title "{/Helvetica=10 +Abnormality Border}"
        The initial hash (the data) could be kept in a separate file and read in by the script. I often find keeping the data and code separate can ease updating/maintainance. Taking it a bit further keeping the actual templates in a separate file wouldn't hurt either.

        When you have it working nicely you would obviously take the white space out.

        updated: added output and included the csv file.

        You need Chart::Gnuplot. It should polish your interface. It is able to generate your chat on the fly. You can apply the loops here.
Re: How to declare variables per loop
by Anonymous Monk on Oct 11, 2011 at 13:52 UTC
      Thank you namesake, I did not read this before.
      It seems there are great caveats against this techique.
      Nevertheless I learned a lot today - from you and from other monks.
      Thanks.
      VE
Re: How to declare variables per loop
by ~~David~~ (Hermit) on Oct 11, 2011 at 14:03 UTC
    You should probably just use a hash instead of a million variables. Something like this would work:
    my $file_location_of; $file_location_of->{abc_this_01} = "C:/Stuff/abc_this_01.csv"; ...
    Or you could actually write a loop to populate the hash. That way you wouldn't have to write a bunch of the same stuff over and over.
Re: How to declare variables per loop
by tospo (Hermit) on Oct 11, 2011 at 14:16 UTC
    Yes, in general, if you want to use lots of variables like that, you actually want a hash or an array. However, in this specific case - as you have presented it - there is no need at all for a variable because the name of the file can be derived from knowing the name of the variable following this simple pattern according to your example:
    C:/Stuff/NAME_OF_VARIABLE.csv
      Thank you tospo,
      this is correct of course, but I thought it would make the code clearer since I do not have to write down all the file names then. Besides I thought I could code the other parameter this way too (e.g. color) - see my answer on dreadpiratepeter a few minutes earlier.
      VE
        except that you do write down all the file names when you assign them to variables. Much better to construct the names of the files on the fly. For example you could do something like
        my $storage_dir = "C:/Stuff/"; foreach my $category qw( abc def ghi ) { foreach my $subcategory qw( this that ){ foreach my $year ( 1..3) { my $file = sprintf("%s_%s_02d.csv", $category, $subcategory,$yea +r); my $path = $storage_dir.$file; # now do something with the file, i.e. generate your plot comman +d do_plot( $path ); } } } sub do_plot { my $path - shift; ### do the actual plotting here }
        Now you don't have to type all the combinations of categories, years etc but let the script handle that. An alternative approach would be to change the way you store the data by putting it all into a single file and starting each block of data with a string that gives yuo all those categories, as in:
        #abc_this_01 -- data goes here --- #def_this_01 -- more data --- etc
        Now your script doesn't need to kow anything about the categories and takes whatever is in your file, which presumably is the output of another script.
Re: How to declare variables per loop
by vagabonding electron (Curate) on Oct 12, 2011 at 13:16 UTC
    Dear All,
    thank you very much for your help! You guys are great.
    I used the solution from wfsp with some modification since $number should be able to be changed. Therefore I took the parts of the solutions of tospo and aartist too. However I gave up the loops with $number because I had to print the title for each plot too. It is readable (imho) in the form of:  my $first = build_command('01', %args);.
    I put the complete script here including the gnuplot part.
    I knew about the module Chart::Gnuplot. Unfortunately I still cannot download modules on my pc at work. Yes I read Yes, even you can use CPAN - none of the solutions works here. The only one were to copy and paste the code but the module has the XS part so it does not work either. I was not able to persuade my manager as proposed in the article. The command structure in the job can be sometimes more tricky as HoH :-)
    Thanks again!
    VE
    use warnings; use strict; my $output = "C:/ .../output.ps"; my $multiplot_title = = qq{set multiplot layout 2, 1 title "The Evalua +tion X" font "Helvetica-Bold,16" set key inside right bottom set xlabel "{/Helvetica=12 Years}" norotate set ylabel "{/Helvetica=12 Results}" rotate by -270 } ; my %args = ( years => q{[2005.5:2011.5]}, yscala => q{[:100]}, sources => [ { source => q{abc}, color => q{#000080}, title => q{"{/Helvetica=10 ABC_Stuff}"}, }, { source => q{def}, color => q{#800080}, title => q{"{/Helvetica=10 DEF_Stuff}"}, }, { source => q{ghi}, color => q{#008000}, title => q{"{/Helvetica=10 GHI_Stuff}"}, }, { source => q{jkl}, color => q{#000000}, title => q{"{/Helvetica=10 JKL_Stuff}"}, }, ], topic => q{this}, abnormality_border => q{mno}, ); my $first = build_command('01', %args); my $second = build_command('02', %args); my $third = build_command('03', %args); my $fourth = build_command('04', %args); my $fifth = build_command('05', %args); my $sixth = build_command('06', %args); sub build_command { my $num = shift; my %args = @_; my $cmd = sprintf( qq{plot %s %s }, $args{years}, $args{yscala} ); for my $source (@{$args{sources}}){ $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" title "" with errorbars lt rgb "%s" l +w 2, }, $source->{source}, $args{topic}, $num, $source->{color}, ); $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" title %s with lines lt rgb "%s" lw 3, + }, $source->{source}, $args{topic}, $num, $source->{title}, $source->{color}, ); } $cmd .= sprintf( qq{"C:/Stuff/%s_%s_%s.csv" with steps lt 1 lw 5 title "{/Helvetica +=10 Abnormality Border}" \n\n\n}, $args{abnormality_border}, $args{topic}, $num, ); return $cmd; } open my $P, "|-", "C:/.../gnuplot" or die; printflush $P qq[ set terminal postscript color portrait set output "$output" set termoption enhanced set encoding iso_8859_1 ###################################### $multiplot_title set title "The First Indicator" $first set title "The Second Indicator" $second unset multiplot ###################################### $multiplot_title set title "The Third Indicator" $third set title "The Fourth Indicator" $fourth unset multiplot ###################################### $multiplot_title set title "The Fifth Indicator" $fifth set title "The Sixth Indicator" $sixth unset multiplot ###################################### ]; close $P;
      Looks good. I would leave it at that but, perhaps for future reference, I couldn't resist a further tweak. :-)
      # ... my %titles = ( q{01} => q{First}, q{02} => q{Second}, q{03} => q{Third}, q{04} => q{Fourth}, q{05} => q{Fifth}, q{06} => q{Sixth}, ); my @cmds; for my $number (qw{01 02 03 04 05 06}){ my $cmd = build_command($number, $titles{$number}, %args); push @cmds, $cmd; } # ...
      sub build_command { my $num = shift; my $title = shift; my %args = @_; my $cmd = sprintf( qq{set title "The %s Indicator\n"}, $title, ); # ....
      ... ###################################### $multiplot_title $cmds[0] $cmds[1] unset multiplot ...
        Thank you very much wfsp.
        After overnight I thought even that it were possible to combine the title for the evaluation and the titles for the diagramms in one hash. One should then input a shorthand for the evaluation at the command line.
        This below is just a fragment, I am tinkering now : -)
        Thanks again!
        VE
        use strict; use warnings; if (!defined $ARGV[0]) { die " Please type the shorthand: THIS for This Great Evaluation THAT for That Small Evaluation "; } my $topic = $ARGV[0]; my %module = ( THIS => [ { name => q{This Great Evaluation}, indicator => { '01' => q{The First}, '02' => q{The Second}, }, }, ], ); print ${module}{$topic}[0]->{name}; print ${module}{$topic}[0]->{indicator}{'01'}; print ${module}{$topic}[0]->{indicator}{'02'};
      My comment is about your Perl Module installation. You can use local::lib and install Perl Module in your home directory. Write the appropriate code using GnuPlot module. Show it to your manager to ask the sys-admin to install that for you in the appropriate directory. Your problem can be more general to lot of people and there has to appropriate solution which gives more freedom to developers and hence better CPAN for all of us.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (6)
As of 2024-03-19 09:25 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found