Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Re: Print the line with the largest number from standard input

by roboticus (Chancellor)
on Jul 22, 2019 at 19:08 UTC ( [id://11103159]=note: print w/replies, xml ) Need Help??


in reply to Print the line with the largest number from standard input

SSSufe:

Sorry for the length of this thing, it's one of my occasional ramble-on nodes. Essentially I'm running and fixing the code incrementally with commentary. If you want to see it in its excruciating detail ....

The first thing I do when programming perl is to add the lines:

use strict; use warnings;

to the top of my scripts. It helps you avoid many common errors. When I did so and ran your code, it said:

$ perl pm_11103133.pl <xy.data Global symbol "$lines" requires explicit package name (did you forget +to declare "my $lines"?) at pm_11103133.pl line 3. Global symbol "@input" requires explicit package name (did you forget +to declare "my @input"?) at pm_11103133.pl line 4. Global symbol "$lines" requires explicit package name (did you forget +to declare "my $lines"?) at pm_11103133.pl line 4. Global symbol "@largest_number" requires explicit package name (did yo +u forget to declare "my @largest_number"?) at pm_11103133.pl line 5. Global symbol "$input" requires explicit package name (did you forget +to declare "my $input"?) at pm_11103133.pl line 5. Global symbol "$lines" requires explicit package name (did you forget +to declare "my $lines"?) at pm_11103133.pl line 6. Execution of pm_11103133.pl aborted due to compilation errors.

It's a bunch of complaints about no variable declarations. So I then added variable declarations to the script, like so:

#!env perl use strict; use warnings; while (my $lines = <STDIN>){ my @input = $lines =~ /\d+/g; my @largest_number = sort {$a <=> $b} @$input; print "$lines\n"; }

Running it again gave the result:

$ perl pm_11103133.pl <xy.data Global symbol "$input" requires explicit package name (did you forget +to declare "my $input"?) at pm_11103133.pl line 7. Execution of pm_11103133.pl aborted due to compilation errors.

Ah, I declared the array @input. But the code is also using the scalar variable $input although it's never assigned a value. So the first error I see in your code is that you're mixing up some variables. Since your code is pretty simple, it's relatively obvious that what you really wanted is to use @input instead of trying to convert the unused scalar variable $input into a list. So a quick fix is to change @$input to @input at the end of the line containing sort. Running it again gives:

$ perl pm_11103133.pl <xy.data Hello, I'm 18 1 this year is 2019 1 1 2 3 - 4

This is obviously not what you want. Looking at the code, it's pretty simple to see why: You're *always* printing $line for every iteration of the loop. That'll never work, since you want only the line with the greatest value in it.

From the code you've got, it seems that you're collecting all the numbers into @input and then sorting them. That looks like a good start. But you need to do a couple of things:

  • You only want to print the line if it has the largest number found.
  • You need to have some history so you can keep track of the largest number you've found so far.

So we'll add that to your script like this:

#!env perl use strict; use warnings; # Keep track of the largest number we've found so far my $largest_number_so_far = 0; while (my $lines = <STDIN>){ my @input = $lines =~ /\d+/g; my @largest_number = sort {$a <=> $b} @input; if ($largest_number[0] > $largest_number_so_far) { # We found a bigger number! print "$lines\n"; $largest_number_so_far = $largest_number[0]; } }

When we run it, we get the result:

$ perl pm_11103133.pl <xy.data Hello, I'm 18 Use of uninitialized value in numeric gt (>) at pm_11103133.pl line 13 +, <STDIN> line 4.

Hmmm ... we have two problems. First, we're printing the wrong line. Obviously 18 is less than 2019 so why are we printing that line and not printing the line containing 2019? Secondly, why are we getting the "Use of uninitialized value" warning?

We need to figure out what the program is "thinking", so we'll add some debugging print statements to show what values the code is looking at, like this:

#!env perl use strict; use warnings; # Keep track of the largest number we've found so far my $largest_number_so_far = 0; while (my $lines = <STDIN>){ my @input = $lines =~ /\d+/g; my @largest_number = sort {$a <=> $b} @input; print "CURRENT LINE: <$lines>\n"; print "NUMBERS FOUND IN LINE: ", join(", ", @largest_number), "\n" +; print "LARGEST IS: $largest_number[0]\n"; if ($largest_number[0] > $largest_number_so_far) { # We found a bigger number! print "$lines\n"; $largest_number_so_far = $largest_number[0]; } }

Now, when we run the code, we get this:

$ perl pm_11103133.pl <xy.data CURRENT LINE: <Hello, I'm 18 > NUMBERS FOUND IN LINE: 18 LARGEST IS: 18 Hello, I'm 18 CURRENT LINE: <1 this year is 2019 1 > NUMBERS FOUND IN LINE: 1, 1, 2019 LARGEST IS: 1 CURRENT LINE: <1 2 3 - 4 > NUMBERS FOUND IN LINE: 1, 2, 3, 4 LARGEST IS: 1 CURRENT LINE: < > NUMBERS FOUND IN LINE: Use of uninitialized value $largest_number[0] in concatenation (.) or +string at pm_11103133.pl line 14, <STDIN> line 4. LARGEST IS: Use of uninitialized value in numeric gt (>) at pm_11103133.pl line 16 +, <STDIN> line 4.

OK, for the first line the program thinks the largest number is 18. That looks just fine. For the next line, though, the code thinks that the largest number is 1. That's clearly wrong! The sort function is sorting the numbers such that the smallest number is first and the largest number is last. There are several ways we could handle this:

  • We could reverse the sort order by changing the sort function like this:
    my @largest_number = sort {$b <=> $a} @input;
  • Or we could use the reverse function to reverse the list:
    my @largest_number = reverse sort {$a <=> $b} @input;
  • Or we could take the largest number from the other end of the list by using $largest_number[-1] instead of $largest_number[0].

The other thing we see is that the "Use of uninitialized value" comes from the blank line at the end. While you could just notice that there's a blank line and ignore it, that's the wrong way to fix it in this case. If you look a little more carefully, the reason for the message is that there are *no* numbers found on that line, which could be normal for your data. So rather than just ignoring a blank line, we'll ignore any line that doesn't have any numbers in it. (Note: You'll notice that we now get the "Use of uninitialized value" twice instead of once. That's to be expected as one of our debug print statements tries to print the value that doesn't exist.)

So we'll make a couple changes to the code, and we get:

$ cat pm_11103133.pl #!env perl use strict; use warnings; # Keep track of the largest number we've found so far my $largest_number_so_far = 0; while (my $lines = <STDIN>){ my @input = $lines =~ /\d+/g; my @largest_number = sort {$b <=> $a} @input; print "CURRENT LINE: <$lines>\n"; print "NUMBERS FOUND IN LINE: ", join(", ", @largest_number), "\n" +; print "LARGEST IS: $largest_number[0]\n"; if (@largest_number) { # We found some numbers on the line! if ($largest_number[0] > $largest_number_so_far) { # We found a bigger number! print "$lines\n"; $largest_number_so_far = $largest_number[0]; } } }

When we run it, the results look better:

$ perl pm_11103133.pl <xy.data CURRENT LINE: <Hello, I'm 18 > NUMBERS FOUND IN LINE: 18 LARGEST IS: 18 Hello, I'm 18 CURRENT LINE: <1 this year is 2019 1 > NUMBERS FOUND IN LINE: 2019, 1, 1 LARGEST IS: 2019 1 this year is 2019 1 CURRENT LINE: <1 2 3 - 4 > NUMBERS FOUND IN LINE: 4, 3, 2, 1 LARGEST IS: 4 CURRENT LINE: < > NUMBERS FOUND IN LINE: Use of uninitialized value $largest_number[0] in concatenation (.) or +string at pm_11103133.pl line 14, <STDIN> line 4. LARGEST IS:

OK, this is much better. We're printing the line with 2019 in it, and we got rid of one of the "use of uninitialized value" lines. We can take out our debugging statements now that they've done their job, and run it again. After doing so, we get:

$ perl pm_11103133.pl <xy.data Hello, I'm 18 1 this year is 2019 1

OK, now *that's* much better. But there's still a problem. We're printing out too many lines! We only wanted to print out the line with 2019, but we got the line with 18 in it too.

The problem is that we're trying to decide whether to print the line too soon. We can't decide to print any particular line until we scan through the entire file, because we never know if a later line will contain a larger number.

To solve that problem, we'll add another history variable to hold the contents of the line that contains the largest number so far. Then only after processing the entire file will we bother to print the line containing the largest number:

$ cat pm_11103133.pl #!env perl use strict; use warnings; # Keep track of the largest number we've found so far my $largest_number_so_far = 0; my $line_with_largest_number; while (my $lines = <STDIN>){ my @input = $lines =~ /\d+/g; my @largest_number = sort {$b <=> $a} @input; if (@largest_number) { # We found some numbers on the line! if ($largest_number[0] > $largest_number_so_far) { # We found a bigger number! $line_with_largest_number = $lines; $largest_number_so_far = $largest_number[0]; } } } print "$line_with_largest_number\n";

When we run it this time, we get the result:

$ perl pm_11103133.pl <xy.data 1 this year is 2019 1

When learning how to program, you eventually develop a feel for what your program is actually doing. You'll get that feel by running your code under the debugger and/or by adding print statements to tell you what the program actually sees. Even after 30 years of programming, I'll still add debug statements to my code to ensure that my understanding of what the code is doing is correct.

Using the debugger is a valuable skill and will help you when your code and/or data gets complicated. You should learn how to use the debugger if you want to learn how to program well. For simple things, print statements are just fine. If you want to see an example of using the debugger, check out this node I did for someone a while ago.

...roboticus

When your only tool is a hammer, all problems look like your thumb.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: note [id://11103159]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others exploiting the Monastery: (6)
As of 2024-04-24 10:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found