Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

Print the line with the largest number from standard input

by SSSufe (Initiate)
on Jul 22, 2019 at 11:21 UTC ( [id://11103133]=perlquestion: print w/replies, xml ) Need Help??

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

Hi, i'm very new to Perl and I've been trying to implement a function that print out the line with the largest number. For example, If the input is:

Hello, i'm 18

1 this year is 2019 1

1 2 3 - 4

The output should beF 1 this year is 2019 1

#!/usr/bin/perl -w while ($lines = <STDIN>){ @input = $lines =~ /\d+/g; @largest_number = sort {$a <=> $b} @$input; print "$lines\n"; }

I know that my code is crappy..I'm struggling with matching the digits in line and also the implementation of this function. Could someone please help me with it?

Another question is what does $line =~ /\-?(?:\d+\.?\d*|\.\d+)/g mean? Could someone please explain this line to me character by character? Appreciate for your help!

Replies are listed 'Best First'.
Re: Print the line with the largest number from standard input (updated)
by AnomalousMonk (Archbishop) on Jul 22, 2019 at 14:21 UTC

    Here's some rather excessively commented code in answer to your OPed question. One line that isn't well commented is
        my $largest_n_in_line = (sort { $a <=> $b } @numbers)[-1];
    What's going on here? (Update: An alternate statement might be
         my ($largest_n_in_line) = sort { $b <=> $a } @numbers;
    In this version, why is the LHS of the assignment enclosed in parentheses? Might it be possible to do this whole operation more simply and perhaps more quickly with an approach based on List::Util::max() instead?)

    All the caveats concerning the regex definition of a "number" noted by TieUpYourCamel here also apply. Also, I've chosen not to use chomp although others have. Why might one use or not use chomp in an application like this?

    I've also chosen to put the input data from your OP into a separate file and redirect the content of the file to STDIN using the  < command line redirection operator. (I'm using Windows, but this should be exactly the same in *nix IIRC.) I've done this because having to re-type a bunch of test input every time you run your program is going to get very annoying very fast. A separate file is easy to edit, and multiple test files can easily be created. (Update: Of course, you can still enter input manually from the console if you wish.)

    Source file line_with_lagest_n_1.pl:

    Invocation:
    c:\@Work\Perl\monks\SSSufe>perl line_with_lagest_n_1.pl < lines.dat F1 this year is 2019 1

    Update: I just noticed that the script file name is given above as line_with_lagest_n_1.pl rather than as line_with_largest_n_1.pl as I had intended. But the posted file name is the way it appears on my system, so I'm not going to bother to change it.


    Give a man a fish:  <%-{-{-{-<

Re: Print the line with the largest number from standard input
by daxim (Curate) on Jul 22, 2019 at 12:21 UTC
    In short: the intent is it matches numbers that look like -1.2, 2.347, 47., -.58 and similar. Flag /g means global, that is to say match several times on the target variable $line, not only once. Read any book or tutorial on regex. There are also many Web sites that analyse and explain regex similar to the output below.
    YAPE-Regex-Explain-4.01› perl examples/explain \-?(?:\d+\.?\d*|\.\d+) The regular expression: (?-imsx:\-?(?:\d+\.?\d*|\.\d+)) matches as follows: NODE EXPLANATION ---------------------------------------------------------------------- (?-imsx: group, but do not capture (case-sensitive) (with ^ and $ matching normally) (with . not matching \n) (matching whitespace and # normally): ---------------------------------------------------------------------- \-? '-' (optional (matching the most amount possible)) ---------------------------------------------------------------------- (?: group, but do not capture: ---------------------------------------------------------------------- \d+ digits (0-9) (1 or more times (matching the most amount possible)) ---------------------------------------------------------------------- \.? '.' (optional (matching the most amount possible)) ---------------------------------------------------------------------- \d* digits (0-9) (0 or more times (matching the most amount possible)) ---------------------------------------------------------------------- | OR ---------------------------------------------------------------------- \. '.' ---------------------------------------------------------------------- \d+ digits (0-9) (1 or more times (matching the most amount possible)) ---------------------------------------------------------------------- ) end of grouping ---------------------------------------------------------------------- ) end of grouping ----------------------------------------------------------------------

      Thank you a lot for your help! This is very helpful!

Re: Print the line with the largest number from standard input
by choroba (Cardinal) on Jul 22, 2019 at 12:45 UTC
    Crossposted to StackOverflow.

    Crossposting is allowed, but it's considered polite to inform about it so people not attending both sites don't waste their efforts solving a problem already solved at the other end of the internet.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]

      Good catch! In fact, this was most likely (imho) a plagiarized post. That is to say, the person who posted it here is not the same person who posted it on SO.

      I appreciate your watchful eye for such things.

Re: Print the line with the largest number from standard input
by clueless newbie (Curate) on Jul 22, 2019 at 14:00 UTC
    #!/usr/bin/env perl use Data::Dumper; # I like to + dump stuff use strict; # "strict" +and "warnings" are a best practice use warnings; my %encountered; # We're goi +ng to "stuff" the numbers encountered here while (<DATA>) { chomp; while (m{(\d+)}g) { # Find numb +ers in the string one set (2019) at a time push @{$encountered{$1}},$_; # Note 1 wi +ll occur twice in the line "1 this year is 2019 1" }; }; # Dump them with a label (and file and line number) so we can verify warn Data::Dumper->Dump([\%encountered],[qw(*encountered)]),' '; # Print them longest first local $"=qq{', '}; #" # To make t +hem more readable for my $number (sort {$b <=> $a} keys %encountered) { # Sort larg +est number first print "$number: '@{$encountered{$number}}'\n"; }; __END__ Hello, i'm 18 1 this year is 2019 1 1 2 3 - 4
    yields
    %encountered = ( '3' => [ '1 2 3 - 4' ], '2' => [ '1 2 3 - 4' ], '18' => [ 'Hello, i\'m 18' ], '4' => [ '1 2 3 - 4' ], '1' => [ '1 this year is 2019 1', '1 this year is 2019 1', '1 2 3 - 4' ], '2019' => [ '1 this year is 2019 1' ] ); at 11103133.pl line 16, <DATA> line 3. 2019: '1 this year is 2019 1' 18: 'Hello, i'm 18' 4: '1 2 3 - 4' 3: '1 2 3 - 4' 2: '1 2 3 - 4' 1: '1 this year is 2019 1', '1 this year is 2019 1', '1 2 3 - 4'
Re: Print the line with the largest number from standard input
by 1nickt (Canon) on Jul 22, 2019 at 12:50 UTC

    Hi, welcome to Perl, the One True Religion.

    You might like the built-in function length.

    You can use it to count the characters in your strings, and then you can keep track of the longest found so far. (Note the use of the __DATA__ section to provide a magical filehandle, and of the angle braces to read its contents into an array of lines, and of chomp to remove the newline character(s) at the end of each line.)

    use strict; use warnings; use feature 'say'; my $longest; my $max_length = 0; my @lines = <DATA>; for my $line (@lines) { chomp($line); my $length = length($line); if ( $length > $max_length ) { $longest = $line; $max_length = $length; } } say "'$longest' was $max_length chars"; __DATA__ Hello, i'm 18 1 this year is 2019 1 1 2 3 - 4

    Hope this helps!


    The way forward always starts with a minimal test.
      SSSufe says they need the line with the largest number, not the longest line, so to expand on 1nickt's solution:
      #/usr/bin/perl use strict; use warnings; use feature 'say'; my $lineWithMax; my $max = 0; my @numbers; my @lines = <DATA>; for my $line (@lines) { chomp($line); @numbers = $line =~ /(\d+)/g; foreach my $number (@numbers) { if ( $number > $max ) { $lineWithMax = $line; $max = $number; } } } say "Line with highest number is '$lineWithMax'"; __DATA__ Hello, i'm 18 1 this year is 2019 1 1 2 3 - 4
      The code works with your sample data, but consider the following: If two or more lines contain the same highest number, it will output only the first one. If a line contains a negative number, it will be treated as a positive one (the "-" will be ignored). Only integers are considered. Numbers written with scientific notation, for example, will not be treated correctly (4 x 105 would be considered less than 40) and decimals would be truncated and the remaining digits treated as a new number.

      Today I learned that captured groupings from a regex can be put in an array.

        ... captured groupings from a regex can be put in an array.

        And they don't even have to be captured. Try
            @numbers = $line =~ /\d+/g;


        Give a man a fish:  <%-{-{-{-<

        Oh, hahah, I guess I did a really really bad job of reading the OP. Well I hope the comment was helpful, this seems far more so!


        The way forward always starts with a minimal test.
Re: Print the line with the largest number from standard input
by roboticus (Chancellor) on Jul 22, 2019 at 19:08 UTC

    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 ....

    ...roboticus

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

Re: Print the line with the largest number from standard input
by BillKSmith (Monsignor) on Jul 23, 2019 at 20:54 UTC

    The use of 'sort' to find a max is always overkill, but can be convenient if efficiency is not an issue. In your case, you probably want 'nsort_by' from the module List::SomeUtils instead of the native 'sort'.

    You should be more explicit in describing what you mean by 'number'. Your code finds only continuous strings of decimal digits. (Note: This ignores negative signs. e.g. -124 > 123)

    An easy way to find the maximum of the numbers on one line is to use the function 'max' from List::Util

    >type SSSufe.pl use strict; use warnings; use List::Util qw(max); use List::SomeUtils qw(nsort_by); my @lines = <DATA>; print ((nsort_by { biggest_on_line($_) } @lines)[-1]); sub biggest_on_line { my $line = shift; my @nums = $line =~ /(\d+)/g; return max @nums; } __DATA__ Hello, i'm 18 1 this year is 2019 1 1 2 3 - 4 >perl SSSufe.pl 1 this year is 2019 1
    Bill

      Nitpick: Because max returns undef when the list is empty (= there is a line with no digits), this code will generate "uninitialized" warnings in that case.

Re: Print the line with the largest number from standard input (updated)
by mr_ron (Chaplain) on Jul 25, 2019 at 14:21 UTC

    Some of the other postings give solutions that are more accessible to a beginner, but I would like to present one that demonstrates some possibilities that are available with more advanced knowledge. If the coding looks too complicated to debug and maintain, you might still be able to mine it for some ideas.

    Update: based on suggestion in reply from haukex coding now does defined test instead of relying on minimum number. I realize that I have been using the technique of creating variables in a conditional (if ... defined ...) for years without really understanding it, until I came across a posting here and read through related discussion. It may be a bit more advanced than I originally thought but ok fit for the revised solution. Satisfied with the brevity of the result but wish it was less complicated. I learned from working on and revising the code, and hope at least a few readers can learn something useful as well.

    #!/usr/bin/perl -n ###################################################################### # Solution requires more advanced knowledge of Perl technology # but leverages "off the shelf" technology and components # for a hopefully short and robust solution. # # -n command line switch provides loop to read file # Number parsing and max number on line come from common # Perl modules. ###################################################################### # # I am being forced to work under threat and duress # and had to remove this. Sorry. Hopefully I can put # it back some day? # # Still afraid of eviction. Harassment at every meal. # Very useful article of clothing seems to have been # stolen day I had to remove code. #

    Invocation:

    ron@penguin:~/monk-bus/line-w-high-no$ ./high-line.pl <in-data.txt 1 this year is 2019 1

    Ron

      If the input file contains only a number -9999998, your code will report that, but if that number is -9999999 or below, it reports "No line with number found."

      Personally, I use undef instead of a magic number; my usual pattern is the following (expanded slightly for clarity):

      my $max; for my $n ( qw/ 6 3 1 4 7 2 10 5 8 9 / ) { if ( !defined $max || $n > $max ) { $max = $n; } } print "Max: ", $max//"none", "\n";

      Update: The suggestion to use undef as a special value actually applies to several other posts in this thread as well, at least here and here. OTOH, your code is the only one that uses a well-established module to find the numbers, there are several posts that simply use the original \d+, which of course will cause the code to think that -99 is larger than 98.

        I updated my post based on your commentary, thank you for reading it and looking at my code. I think the resulting change was more complicated than the example in your reply, but I felt that was necessary to fit with the surrounding code and keep my solution short.

        Ron

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2024-03-29 08:57 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found