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

Hi, im quite new to perl and im very keen to learn because im picking it up quite quickly! im just writing to ask if this is the best code for what i want to do. i need to display a table of some output, like so:
Range 10-20 20-30 30-40 Count 0 2 1 Percent 0 67 33
there's a bit more to it but it wont fit horizontally across the screen. so anyway, here's my code. it accesses a hash of hashes:
$range1=1; $range2=10; $range3=1; $range4=10; $person_count=0; print("Range\t10-20\t20-30\t30-40\n"); print("Count"); for ($counter=0;$counter<2;$counter++) { $range3=$range3+10; $range4=$range4+10; print("\t ".personCount($range3,$range4)); } print("\n"); print("Percent\t"); for ($counter=0;$counter<2;$counter++) { $range1=$range1+10; $range2=$range2+10; printf(" %.0f\t",(percent($range1,$range2))); } print("\n");
what i have works but im not sure if its the most efficient or clean as i can get. id love your comments!

Replies are listed 'Best First'.
Re: Output to table
by davido (Cardinal) on Jan 26, 2006 at 01:10 UTC

    Good, you're learning to "get things done" with Perl. That's one of the first steps; hash out a problem and solve it. That's what Perl is for, and you're on the right track. There is some room for improvement, and if you're open minded you should read on (otherwise, stop reading right now).

    Here is one problem: You're calling the ranges 10-20, 20-30, and 30-40. That means by label convention you've got edge cases where the same item could fall into two ranges. For example, 20 fits into the 10-20 range and the 20-30 range. Of course behind the scenes your code is not actually placing the same item into two buckets, but the labels you're using will mislead the user.

    Let's assume you want the ranges really to be 10-19, 20-29, and 30-39. And we'll also assume that "range" is an integer.

    Your next problem is that you'll soon run out of gas and start asking us about symbolic references if you use variable names like $range1, $range2, and $range3. As soon as you start numbering variable names, you should ask yourself if an array wouldn't be a better solution. (Hint; when you do get to that time in your life where you want to ask about using symbolic references to handle variable variable names, we're full of hasty retorts as to why you shouldn't be asking that question. *grin*)

    Next, you're using a C-style 'for' loop just to iterate from '0' to '1'. Break out of that habit. Consider foreach(0..1) ...this is Perl. The C-style loops exist in Perl's syntax, and occasionally are actually useful, but more often than not there's a clearer way to write your code.

    Another thing to learn is to adhere to strictures. This means that all of your variables should be declared, and unless there's a good reason, they'll be declared as lexicals using my(). Down the road you'll be glad you learned how to comply with use strict; and use warnings;

    I would love to provide an example of how I would re-write this, but you haven't shown us the definition of personCount() and percent(), so I'm not sure what's going on in those little black boxes.


    Dave

      Cheers for the help dave! here's my entire code:
      CustData.txt --------------------------------------------- 1001,Lord,Flintstone,Fred,M,11/01/1980,99 1002,Lady,Flintstone,Wilma,F,11/03/1988,51 1003,Miss,Flintstone,Pebbles,F,01/01/1999,55 ---------------------------------------------
      #!usr/local/bin/perl my $path="CustData.txt"; open(DATA, "<$path") || die "Couldn't open $path for reading: $!\n"; while (<DATA>) { chomp; my ($id, $title, $surname, $forename, $sex, $dob, $vision) = split +(/,/); $HoH{$id} = {'title' => $title, 'surname' => $surname, 'forename' +=> $forename, 'sex' => $sex, 'dob' => $dob, 'vision' => $vision,}; } close(DATA); sub personCount { ($x,$y) = @_; my ($vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return int($person_count); } sub percent { ($x,$y) = @_; $percent = 0; my ($total_person_count,$vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { ++$total_person_count; } foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return $percent = ($person_count/$total_person_count)*100; } print("\nDo you want to print to screen of to a file? [s] or [f]: "); $question=<STDIN>; while ($question !~ m/[sSfF]/) { print("Please type [s] or [f]\n"); } if ($question =~ m/[sS]/) { $range1=1; $range2=10; $range3=1; $range4=10; $person_count=0; print("\n"); print("Range\t01-10\t11-20\t21-30\t31-40\t41-50\t51-60\t61-70\t71- +80\t81-90\t91-99\n"); print("Count"); for ($counter=0;$counter<10;$counter++) { $range3=$range3+10; $range4=$range4+10; print ("\t ".personCount($range3,$range4)); } print("\n"); print("Percent\t"); for ($counter=0;$counter<10;$counter++) { $range1=$range1+10; $range2=$range2+10; printf(" %.0f\t",(percent($range1,$range2))); } print("\n\n"); } elsif ($question =~ m/[fF]/) { FILENAME: { print("\nFilename: "); chomp($filename=<STDIN>); while ($filename !~ m/^[a-zA-Z]{1}\w{2,7}$/ || $filename =~ m/ +\./) { print("\nInvalid filename.\n Please try again.\n"); redo FILENAME; } if (-e $path) { print("File exists. Overwrite? [y] or [n]: "); $exists=<STDIN>; while ($exists !~ m/[yYnN]/) { print("Please type [y] or [n]\n"); } if ($exists =~ m/[nN]/) { redo FILENAME; } else { print("\n[Saved as ".$filename.".txt]\n\n"); } } else { print("\n[Saved as ".$filename.".txt]\n\n"); } } my $path=($filename.".txt"); open (DATA, ">$path") || die "\nCouldn't open $path for writing: $ +!\n"; $range1=1; $range2=10; $range3=1; $range4=10; $person_count=0; print DATA ("Range\t01-10\t11-20\t21-30\t31-40\t41-50\t51-60\t +61-70\t71-80\t81-90\t91-99 \n"); print DATA ("Count"); for ($counter=0;$counter<10;$counter++) { $range3=$range3+10; $range4=$range4+10; print DATA ("\t ".personCount($range3,$range4)); } print DATA ("\n"); print DATA ("Percent\t"); for ($counter=0;$counter<10;$counter++) { $range1=$range1+10; $range2=$range2+10; printf DATA (" %.0f\t",(percent($range1,$range2))); } print DATA ("\n"); close(DATA); }
Re: Output to table
by saintmike (Vicar) on Jan 26, 2006 at 00:18 UTC
      rather not use modules, sorry. i want to be able to write it myself.
        rather not use modules, sorry. i want to be able to write it myself.
        Sure, it's a nice learning experience to write something yourself. Two comments:
        • format isn't a module. It comes with the perl core.
        • It's usually a waste of time and resources to solve a problem that has already been solved. If a module is readily available on CPAN, it's usually a good idea to use it, because chances are that it has been community-tested (no guarantees, though) and it's difficult to come up with a better solution if you start from scratch.
Re: Output to table
by chargrill (Parson) on Jan 26, 2006 at 00:08 UTC
    Before commenting on the code shown, I'd like to see the personCount sub...

    -- chargrill

    $/ = q#(\w)# ; sub sig { print scalar reverse join ' ', @_ } + sig map { s$\$/\$/$\$2\$1$g && $_ } split( ' ', ",erckha rlPe erthnoa stJu +" );
      the whole code:
      #!usr/local/bin/perl my $path='CustData.txt'; open(DATA, "<$path") || die "Couldn't open $path for reading: $!\n"; while (<DATA>) { chomp; my ($id, $title, $surname, $forename, $sex, $dob, $vision) = split +(/,/); $HoH{$id} = {'title' => $title, 'surname' => $surname, 'forename' +=> $forename, 'sex' => $sex, 'dob' => $dob, 'vision' => $vision,}; } close(DATA); $range1=1; $range2=10; $range3=1; $range4=10; $person_count=0; print("Range\t10-20\t20-30\t30-40\t41-50\t51-60\t61-70\t71-80\t81-90\t +91-99\n"); print("Count"); for ($counter=0;$counter<9;$counter++) { $range3=$range3+10; $range4=$range4+10; print("\t ".personCount($range3,$range4)); } print("\n"); print("Percent\t"); for ($counter=0;$counter<9;$counter++) { $range1=$range1+10; $range2=$range2+10; printf(" %.0f\t",(percent($range1,$range2))); } print("\n"); sub personCount { ($x,$y) = @_; my ($vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return int($person_count); } sub percent { ($x,$y) = @_; $percent = 0; my ($total_person_count,$vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { ++$total_person_count; } foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return $percent = ($person_count/$total_person_count)*100; }
Re: Output to table
by stonecolddevin (Parson) on Jan 26, 2006 at 00:19 UTC
    I don't see a HoH anywhere. Could you post that?
    meh.
      the whole code:
      #!usr/local/bin/perl my $path='CustData.txt'; open(DATA, "<$path") || die "Couldn't open $path for reading: $!\n"; while (<DATA>) { chomp; my ($id, $title, $surname, $forename, $sex, $dob, $vision) = split +(/,/); $HoH{$id} = {'title' => $title, 'surname' => $surname, 'forename' +=> $forename, 'sex' => $sex, 'dob' => $dob, 'vision' => $vision,}; } close(DATA); $range1=1; $range2=10; $range3=1; $range4=10; $person_count=0; print("Range\t10-20\t20-30\t30-40\t41-50\t51-60\t61-70\t71-80\t81-90\t +91-99\n"); print("Count"); for ($counter=0;$counter<9;$counter++) { $range3=$range3+10; $range4=$range4+10; print("\t ".personCount($range3,$range4)); } print("\n"); print("Percent\t"); for ($counter=0;$counter<9;$counter++) { $range1=$range1+10; $range2=$range2+10; printf(" %.0f\t",(percent($range1,$range2))); } print("\n"); sub personCount { ($x,$y) = @_; my ($vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return int($person_count); } sub percent { ($x,$y) = @_; $percent = 0; my ($total_person_count,$vision_total,$person_count)=0; foreach my $id (sort keys %HoH) { ++$total_person_count; } foreach my $id (sort keys %HoH) { if( ($HoH{$id}->{'vision'}>=$x) & ($HoH{$id}->{'vision'}<=$y) +) { ++$person_count; $vision_total=$vision_total+$HoH{$id}->{'vision'}; } } return $percent = ($person_count/$total_person_count)*100; }