http://qs321.pair.com?node_id=198158

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

I've written a very basic program that reads one long line of data from an external file and the data is supposed to be sorted and then displayed. Very basic stuff here. I wrote the code without using any sort opperator, because I wanted a change. My output isn't quite what I'm expecting and for the life of me I cannot locate the problem....Any ideas?

The basic code for the program is as follows::

open INPUT, "a:scores.txt" || die "Cannot locate input file: $!"; while (<INPUT>){ $ctr = 0; chomp; ($class, @array) = split /:/; foreach(@array){ ($names[$ctr],$score[$ctr]) = split /,\s+/; $ctr++; } } $control = @score; for ($ctr=0; $ctr<$control; $ctr++){ for($sub=$control-1; $sub>$ctr; $sub--){ if ($score[$sub]<$score[$sub-1]){ $temp = $score[$sub]; $score[$sub] = $score[$sub-1]; $score[$sub-1] = $temp; $temp2 = $names[$sub]; $names[$sub] = $names[$sub-1]; $names[$sub-1] = $tem; } } } foreach(@score){ $total += $score[$ct]; $ct++; } $avg = $total/$ct; print "\n\n\tThe class $class has $control students:\n\n"; print "\tThe lowest test score was: $score[1]\n"; print "\tThe highest test score was: $score[$control-1]\n"; print "\tThe average test score was:"; printf ("%.2f %s", $avg,"\n\n"); for ($ctr=0; $ctr<=$control; $ctr++){ print "\t$names[$ctr] scored $scores[$ctr]\n"; }
My input file is as follows:

Perl Programming:Joe Smith, 87:Jane Thompson, 93:Ed Jones, 75:Nancy Edwards, 84:James Allen, 64:Peggy Johnson, 68:Bill Swanson, 98:Thomas Jefferson, 76:Abraham Lincoln, 71:John Adams, 84:Theodore Roosevelt, 66:John F. Kennedy, 80:Jimmy Carter, 75:Ronald Reagan, 97:Grover Cleveland, 95:Dwight D. Eisenhower, 96:George Bush, 94:

The desired output I am hoping for is:
Line 1 -- A header displayed with class title and # of students.
Line 2 -- Lowest score on test
Line 3 -- Highest score
Line 4 -- Average
Line 5 -- blank
Line 6+   (Name of student) scored (score) sorted from low score to high score.
Currently my output is very scattered. Only 3 students are displayed, and no scores are displayed other than High/low/average. I believe my pseudo-sort is funtioning...but I'm unsure. I've stared aimlessly at this code for 2 hours now and thought maybe I should post if for all the "gurus" to glance at it and fix my problem in 12 seconds.

Thanks in advance Great and Mighty Monks!!!

Replies are listed 'Best First'.
Re: I/O problems...what am I missing?
by sauoq (Abbot) on Sep 16, 2002 at 05:58 UTC
               $names[$sub-1] = $tem;

    That's a typo. You probably want $names[$sub-1] = $temp2;

        print "\t$names[$ctr] scored $scores[$ctr]\n";

    Another typo. Your array is named @score not @scores

    Since you are splitting on colons, you might find an empty field at the end of your data. I don't think that will break anything in this case though.

    -sauoq
    "My two cents aren't worth a dime.";
    
Re: I/O problems...what am I missing?
by dws (Chancellor) on Sep 16, 2002 at 06:12 UTC
    My output isn't quite what I'm expecting and for the life of me I cannot locate the problem....Any ideas?

    use strict;

    And the problem will jump right off the page.

      use strict;
      And the problem will jump right off the page.
      I'm sorry, that's just wrong. A use strict would cause 60 completely unrelated and spurious errors to "jump right off the page" at our poor unsuspecting programmer. It wouldn't be helpful at all and probably counter-productive.

      He'd probably do what every new programmer does when they see this: declare everything with my until the errors go away. Which means he'd probably declare the wrong variable names with my too in the massive effort to track down and declare all of his variables. So in effect he'd be using strict for no good reason, waste his time, cluttered his code, and still not found his error. Looking at this script he probably doesn't need the benefits of lexicals anyway.

      Turn on warnings instead. A -w in the #! line or a use warnings would make the problem a lot more obvious. In fact, perl tells you *exactly* what the problem is. And finds another one for you as well.


      PS to the original poster: the codeopen INPUT, "a:scores.txt" || die "Cannot locate input file: $!"; has the wrong precedence and doesn't do what you think it does. Try an "or" instead of "||".
        I'm sorry, that's just wrong. A use strict would cause 60 completely unrelated and spurious errors to "jump right off the page" at our poor unsuspecting programmer. It wouldn't be helpful at all and probably counter-productive.

        Nonesense. I copied the code into an editor, looked at it for a moment, added use strict;, and it took less than a minute and a half to work through the "unrelated and spurious" errors before identifying the culprit. If he'd started off strict, he (probably) never would have had this problem.

        Turn on warnings instead. A -w in the #! line or a use warnings would make the problem a lot more obvious.

        That's good advice, after problems identified by use strict; have been cleaned up. To jump right into -w is kind of like debugging stuff at run-time that still has compile-time problems.

Re: I/O problems...what am I missing?
by kabel (Chaplain) on Sep 16, 2002 at 06:04 UTC
    short addition: "use strict" and "use warnings" help prevent such errors. for the whole script: perhaps a hash or a AoA suits better for the given input data. that makes a sort much easier.
Re: I/O problems...what am I missing?
by csotzing (Sexton) on Sep 16, 2002 at 10:53 UTC
    $control = @score; should be $control = $#score + 1; to get the size of the array (I believe that's what you're trying to do...

    As said before, $names[$sub-1] = $tem; should be $names[$sub-1] = $temp2;

    You also have an error in this for loop... (You need to use your $ctr, and reset it to 0 before the loop.)
    $ctr = 0; foreach(@score){ $total += $score[$ctr]; $ctr++; }


    The average score should be ($total / $control) or ($total / $ctr), lowest test score should be $score[0], and the highest should be $score[$control - 1] or $score[$ctr - 1]
    print(map(lc(chr),split(6,qw/99672682673683684689632658645641610607/)));

      "$control = @score; should be $control = $#score + 1; to get the size of the array"

      These statements are equivalent (since evaluating an array in a scalar context gives its length). Camel 3rd ed. p76 refers.

      Some have argued that the former is preferable (e.g. see MJD's comments).

        You guys are my heroes! I love you! Thanks