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

Hi there I'm attempting to write a perl script that opens a directory from the command line, opens each file at a time and then according to the information extracted from each line in the text file, stores how many times a set of numbers within a particular 'range' occurs in the text.
So, for example, if I want to count how many times x occurs with a value between 180-175 occurs when y = any value between 170-165, I want to store this information in a array identifying this value range and then print off the total number of times the desired x,y range occurs at the end. Here is a snippet of my code thus far:
$underscore = "_"; for($i = -180; $i < 181; $i = $i+5) { $j = $i+5; if(($x >= -180) && ($x < -175) && ($y >= $i) && ($y < $i+5 )) { $k++; $x1 = "substr($i, 1, 3)"; $x2 = "substr($i, 1, 3)"; $y1 = substr($i, 1, 3); $y2= substr($j, 1, 3); # attempting to name array according to value range $array = "array$underscore$x1$underscore$x2$underscore$y1$unde +rscore$y2"; $count = $count +1; $array[$k] = ($count); print "$array[$k]";
This doesnt seem to work at all as I am unable to get the array value to print out. Eleswhere, the array wont print out when I use print @array either. I can only assume I'm not naming it correctly or something. Any help would be much appreciated

Replies are listed 'Best First'.
Re: headache with arrays.
by davido (Cardinal) on Sep 20, 2004 at 15:46 UTC

    A few problems, and they're all in the "Perl will give you enough rope to hang yourself" category.... $x1 and $x2 are going to be set to the literal text "substr(20,1,3)" (for example... the 20 could be any number in the range of -180 and 180). Why would you want substr to appear as literal text?

    $y1 is going to be set to the string value of the 2nd digit from the left of $i, extending for three digits to the right. That means when $i is negative, the minus sign is left off. When it's positive, the high-order digit is left off. The same problem applies to the $y2 assignment. *sigh*

    $array = "array$underscore$x1......etc" looks to me like you're trying to create a symbolic reference. Don't bother, that is a road to madness. Use real references instead, or a hash whos keys are this concaucted name.

    And if you did absolutely have to muck up your symbol table with symbolic references, the syntax would be ${$array}[$k] = $count;, though I'm telling you, that's not what you want to be doing in your early stages of Perl learning...and probably not even in the later stages either.

    You can read up on Perl's references at perlref and perlreftut. String quoting interpolation is covered in perlop. And of course there's substr.


    Dave

Re: headache with arrays.
by Roy Johnson (Monsignor) on Sep 20, 2004 at 16:29 UTC
    I've written some code that (I think) does what you want. Being new to Perl, it will probably take you some study before you understand the nested structures (array of arrays, which are done with references). I recommend perldoc perlreftut.
    # This is an "array of arrays". Each row is one set of ranges, # a pair of Xs and a pair of Ys my @ranges = ([-180, -170, -130, -120], [-180, -170, -110, -105]); # This array is going to store the counts for each row of @ranges # It is initialized to zero for each row my @range_counts = (0) x @ranges; # This reads default input: file(s) on command-line or STDIN while (<>) { # split the line on whitespace my ($x, $y) = split; # Count through the rows in @ranges for my $R (0..$#ranges) { # Compare if ($x >= $ranges[$R][0] and $x < $ranges[$R][1] and $y >= $ranges[$R][2] and $y < $ranges[$R][3]) { # Increment counter ++$range_counts[$R]; } } } # Go through the rows in @ranges again, printing them and the # corresponding count for my $R (0..$#ranges) { printf "(%d..%d, %d..%d): %d\n", @{$ranges[$R]}, $range_counts[$R]; }

    Caution: Contents may have been coded under pressure.
      Thank you very much. I shall give it a go and tell you how I got on :)
Re: headache with arrays.
by dragonchild (Archbishop) on Sep 20, 2004 at 15:47 UTC
    You have more headaches than you think or are (probably) capable of dealing with, given my assumption of your relatively-new Perl skills. You're entering problems with symbolic references, variable naming, and a host of other issues.

    I would strongly suggest you go into a little more detail about what you're trying to do and let us help you with how you might want to do it. Your requirements are a little vague.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Then there are Damian modules.... *sigh* ... that's not about being less-lazy -- that's about being on some really good drugs -- you know, there is no spoon. - flyingmoose

    I shouldn't have to say this, but any code, unless otherwise stated, is untested

Re: headache with arrays.
by Anonymous Monk on Sep 20, 2004 at 16:01 UTC
    Hello again Thanks for the help so far. What I actually want to do is the following. I have a text file which looks vaguely like this:
    -175 -120 -175 -135
    etc ... and what I want to do is to count the number of times that, far example, the first value is within the range -180 to -170 when the second value is within the range -120 to -130. So, in this example, only the top entry is within the required range. I have a number of 'ranges' to look at in the same text file, so I thought it might be a good idea to add all the times a particular value 'set' was within a particular range and then add it to a particular array (appropriately named) and then count them all at the end. Anyway, thats what I thought. If anyone could suggest a good way of tackling this properly, it would be very much appreciated.
      here is a functional program to get you started...

      init a counter open the file and read it line by line split each line on space to get an array of the fvalue and svalue increment the counter if fvalue is within the range AND svalue is with +in the range close the file

      It is really easy so I hope you will find it fun to convert the above into Perl.

      Good luck.

      update: You can even avoid the splitting in line 3 by using a simple regexp to get the fvalue and svalue. Might result in a possible speed increase, and will eliminate one spurious variable creation.

      Unless you need to do something else with all the matches at the end, I'm not sure why you'd save them all in an array. Just count. Example:
      my( $x_lo, $x_hi ) = ( -180, -170 ); my( $y_lo, $y_hi ) = ( -130, -120 ); my $count = 0; while (<>) { my( $x, $y ) = split; if ( $x >= $x_lo && $x <= $x_hi && $y >= $y_lo && $y <= $y_hi ) { $count++; } } print "$count matches.\n";
      If you actually needed the matches, it's simple enough to adapt the above to stick them on an array:
      my( $x_lo, $x_hi ) = ( -180, -170 ); my( $y_lo, $y_hi ) = ( -130, -120 ); my @matches; while (<>) { my( $x, $y ) = split; push @matches, [ $x, $y ] if $x >= $x_lo # slighly different syntax, && $x <= $x_hi # just to mix things up. :-) && $y >= $y_lo && $y <= $y_hi; } print scalar(@matches), " matches.\n";
      And here's a golfish version:
      my( $x_lo, $x_hi ) = ( -180, -170 ); my( $y_lo, $y_hi ) = ( -130, -120 ); my @matches = grep { $_->[0] >= $x_lo && $_->[0] <= $x_hi && $_->[1] >= $y_lo && $_->[1] <= $y_hi } map [ split ], <>;