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

How would you add a variable to an array name to use in a statement? I need to make sure it runs 3 times if $count > 2000. Code is below:
$count = 0; $test_num = 0; # Finds district for all stores foreach $dist_fld (@all_stores) { if ($count < 998) { my $new = $dist_fld.","; print "$new\n"; push(@store_get_dist1,$new); $test_num + 1; } if ($count < 1998) { my $new = $dist_fld.","; print "$new\n"; push(@store_get_dist2,$new); $test_num + 1; } if ($count < 2998) { my $new = $dist_fld.","; print "$new\n"; push(@store_get_dist3,$new); $test_num + 1; } } print "Number of queries needed: $test_num\n"; for ($i = 1; $i < $test_num; $i++) { my $last_one = pop(@store_get_dist${i}); #error occurs here chop($last_one); push(@store_get_dist${i}, $last_one); #error occurs here # GRAB ALL STORE, DISTRICT, REGION AND GROUP info $get_district = "SELECT distinct(DIST_I)\n". "FROM BCE_STORE\n". "WHERE LOC_I in (@store_get_dist${i})"; $grab_dist = $dbv->prepare("$get_district"); ### Execute the statement in the database $grab_dist->execute; print "$get_district\n"; while ( my $row = $grab_dist->fetchrow( ) ) { print "$row\n"; } }
Thanks in advance... Sincerely, Paul

Replies are listed 'Best First'.
Re: adding number to array name
by davido (Cardinal) on Aug 23, 2006 at 23:18 UTC

    Let me say this yet again: What you're asking about is how to create symbolic references; ie, how to manipulate variables' names programatically (variable variable names). Perl allows you to do this. But it is broadly regarded as the wrong way to accomplish what you actually need to do. You're better off using proper references, specifically, a list of lists. You can read about how to do that in perlref, perlreftut, perllol, and perldsc.

    Many newcomers think that they've come up with one of those situations where symbolic references are a good idea. They're almost universally wrong. Computer Science has developed for a half of a century in a direction that separates a program's data from the program's source itself. Variable variable names (symbolic references) muddle that distinction, possibly with unhappy results. While there are a few useful uses of symbolic references, symbol table manipulation is best left to a few experts. Just as you don't need to know how to build a microprocessor in order to use a microprocessor, you don't need to worry about manipulating the symbol table in order to benefit from its existence. To be frank, you haven't come up with a new good reason to use symbolic references. It's been addressed, rehashed, and meditated upon. The problem's been solved.

    So when you feel the temptation to write, "@array1" and "@array2", or @{$array . $i}, you should instead be doing something like this:

    $array->[1][4] # for a multidimensional array

    Symbolic references are illegal under "use strict;", for good reason. They are error prone, and the errors they foster can be very difficult to pinpoint, and in fact, not always immediately apparent. You may not discover an error caused by misuse of symbolic references until code has been in place for a long time. Then one day you'll stumble across that special case that awakens the demon. Other times, the bugs can crop up immediately, but be extremely difficult to figure out where the offending code is.

    Proper references (hard references) are easier to manage, and in most regards, more powerful. They are the basis for complex datastructures in Perl, and usually the facilitators of objects in Perl as well. They're very flexible, and yet, they don't blur the distinction between code and data, and they are unlikely to produce the kind of hard to find bugs that soft refrences can hide.


    Dave

      Computer Science has developed for a half of a century in a direction that separates a program's data from the program's source itself.

      ... except for Lisp and derivatives and successors, but I think you meant to say instead that names are for programmers and programmers alone.

Re: adding number to array name
by GrandFather (Saint) on Aug 23, 2006 at 22:06 UTC

    There are a slew of problems in this sample. For a start I strongly recommend that you add use strict; use warnings; to all the Perl scripts you write or maintain!

    Your immediate problem is solved by using an array of arrays. The syntax changes from @store_get_dist1 to @{$store_get_dist[1]}.

    Now for the real issues:

    • $test_num + 1; does nothing. You may have intended $test_num += 1;, but that probably is not right either.
    • $count is never adjusted so all three if statements have their bodies executed.
    • Even if count were incremented each time through the loop it is likely that you still won't get the behaviour you expect. See below for a possible solution.
    • In Perl there is seldom need to write C for loops. Try for my $i (1 .. $test_num) { instead.

    The following may be a little closer to what you require (untested):

    use strict; use warnings; my @all_stores; my @store_get_dist; my $dbv; my $count = 0; my $test_num = 1; # Finds district for all stores foreach my $dist_fld (@all_stores) { $test_num++ if ++$count =~ /(\d*)998/; my $new = $dist_fld.","; print "$new\n"; push(@{$store_get_dist[$test_num]},$new); } print "Number of queries needed: $test_num\n"; for my $i (1 .. $test_num) { my $last_one = pop @{$store_get_dist[$i]}; #error occurs here chop($last_one); push (@{$store_get_dist[$i]}, $last_one); #error occurs here # GRAB ALL STORE, DISTRICT, REGION AND GROUP info my $get_district = "SELECT distinct(DIST_I)\n". "FROM BCE_STORE\n". "WHERE LOC_I in (@store_get_dist${i})"; my $grab_dist = $dbv->prepare("$get_district"); ### Execute the statement in the database $grab_dist->execute; print "$get_district\n"; while ( my $row = $grab_dist->fetchrow( ) ) { print "$row\n"; } }

    Update opps, @{$store_get_dist[1]} -> @{$store_get_dist[$test_num]}


    DWIM is Perl's answer to Gödel
      Sorry if I misunderstood the orginal post.
      From the orginal post, if count is less than 998 value will be added to @store_get_dist1, @store_get_dist2 and @store_get_dist3.
      If the count is less than 1998 and greater than or equal to 998, the value will be addded to both @store_get_dist2 and @store_get_dist3.
      At the time when the count is 1998, there should be 997 elements in @store_get_dist1 and 1997 elements in @store_get_dist2.
      In your array of array implementation, When I add
      print scalar(@{$store_get_dist[1]});
      it prints 997 and prints 1000 for index 2. Not sure if this is the expected behaviour.
      It should have been 997 and 1997 respectively. Am I missing anything?

      Update:
      From
      push(@{$store_get_dist[$test_num]},$new);
      to
      for ($test_num.. int((($#all_stores+3)/1000)+1)) { #3 to account for 998 1998 and 2998 push(@{$store_get_dist[$_]},$new); }
      and it should work. A quick dirty hack though.

        The behaviour implied by the original code is sufficiently odd that I assumed that it was in error rather than by design, especially in light of some of the other things that clearly were errors such as the additions in void context.

        It seemed much more likely that the data was intended to be partitioned into different groups than that groups with replicated information were required.


        DWIM is Perl's answer to Gödel
Re: adding number to array name
by shmem (Chancellor) on Aug 23, 2006 at 21:47 UTC
    Like so:
    my $last_one = pop(@{'store_get_dist'.$i});

    update But... I'd not do it that way. I'd make an array of anonymous arrays and say rather

    my $last_one = pop(@{$store_get_dist[$i]});

    than using symbolic references...

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
        Of course. First I answered the OPs question, thenI was completing the node with my objection to the approach. You caught me whilst editing ;-)

        Maybe the other way round it's better - objection first?

        --shmem

        _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                      /\_¯/(q    /
        ----------------------------  \__(m.====·.(_("always off the crowd"))."·
        ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: adding number to array name
by Anonymous Monk on Aug 23, 2006 at 21:44 UTC
    For got to add "$count++;" before the foreach closing bracket.... Thanks again...