http://www.perlmonks.org?node_id=1216404

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

Consider the following list of variables:
11673326 11673329 11673325 11673330 11673321 11673335

I'm trying to write a function which finds the next larger number (larger than the argument).
Example: If the input if 11673326 the output is 11673329, if the input is 11673335 the variable should be undefined, if input is 11673321 the output should be 11673325.
What I have tried:

my $current = 11673326 my ($next,@status); @status = <DATA>; $next = $current; foreach my $row (@status) { chomp($row); if(looks_like_number($row) && $current < $row && ($next == $cu +rrent || $next > $row)) { $next = $row; } } __DATA__ 11673326 11673329 11673325 11673330 11673321 11673335

But it doesn't quite work. Where is the problem and how to solve it? Is there a better solution? I prefer not to use any other functions or modules. Can I do it some how with regex?

Replies are listed 'Best First'.
Re: get next higher number
by choroba (Cardinal) on Jun 11, 2018 at 16:50 UTC
    Sort the values before you compare them to the argument. You can then safely return the first value that satisfies the condition:
    #!/usr/bin/perl use warnings; use strict; my @values = qw( 11673326 11673329 11673325 11673330 11673321 11673335 + ); my @sorted = sort { $a <=> $b } @values; sub next_higher { my ($from) = @_; for (@sorted) { return $_ if $_ > $from; } return } use Test::More; is next_higher(11673326), 11673329; is next_higher(11673335), undef; is next_higher(11673321), 11673325;

    Or just remember the closest number so far (and handle the case where it's not yet defined):

    #!/usr/bin/perl use warnings; use strict; my $data_start = tell *DATA; sub next_higher { my ($from) = @_; seek *DATA, $data_start, 0; my $so_far; while (<DATA>) { chomp; $so_far = $_ if $_ > $from && (! defined $so_far || $_ < $so_f +ar); } return $so_far } use Test::More; is next_higher(11673326), 11673329; is next_higher(11673335), undef; is next_higher(11673321), 11673325; __DATA__ 11673326 11673329 11673325 11673330 11673321 11673335

    ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: get next higher number
by Limbic~Region (Chancellor) on Jun 11, 2018 at 16:48 UTC
    ovedpo15,
    What you are trying to do is find an item in a list and then return the item that comes after it. That's relatively simple but you should be able to deal with things like unordered lists, duplicate items, etc. if those things matter. If you know you will always have an ordered list with unique entries - you could turn this into a binary search:
    my @status; while (<DATA>) { chomp; push @status, $_; } my $current = 11673326; my $next = find_next_item($current, \@status); die "'$current' is either not found or has no next item\n" if ! define +d $next; print "The next number after '$current' is '$next'\n"; sub find_next_item { my ($item, $list) = @_; my $next; for my $idx (0 .. $#$list) { if ($list->[$idx] eq $item) { $next = $list->[$idx + 1] if $idx <= $#$list; last; } } return $next; }
    The code above is untested. I haven't coded for several years now but I think it is close.

    Cheers - L~R

Re: get next higher number
by hippo (Bishop) on Jun 11, 2018 at 18:11 UTC
    But it doesn't quite work.

    Doesn't quite work? It doesn't even compile.

    $ perl -cw 1216404.pl syntax error at 1216404.pl line 2, near "my " 1216404.pl had compilation errors.

    Even if it did compile how would you know if it works since it produces no output? See How to ask better questions using Test::More and sample data

    Where is the problem and how to solve it?

    Line 1 is missing a trailing semi-colon. Add one.

    Is there a better solution?

    Almost certainly but that would depend on in which way you would want them to be better.

    I prefer not to use any other functions or modules.

    If this is your definition of "better" then I will gladly leave you to it.

    Can I do it some how with regex?

    It would be very illuminating to hear your reasoning for asking this.

Re: get next higher number
by thanos1983 (Parson) on Jun 11, 2018 at 16:45 UTC

    Hello ovedpo15,

    You can use the module List::Util::min and do something like that:

    #!/usr/bin/env perl use strict; use warnings; use List::Util 'min'; my @arr = ( 11673326, 11673329, 11673325, 11673330, 11673321, 11673335 ); print "Please insert a number:\n"; chomp(my $number = <>); my $next_largest = min grep $_ > $number, @arr; if(defined($next_largest)) { print $next_largest."\n"; } else { print "undef\n"; } __END__ $ perl test.pl Please insert a number: 11673326 11673329 $ perl test.pl Please insert a number: 11673321 11673325 $ perl test.pl Please insert a number: 11673335 undef

    Hope this helps, BR

    Seeking for Perl wisdom...on the process of learning...not there...yet!
Re: get next higher number
by pryrt (Abbot) on Jun 11, 2018 at 16:57 UTC

    edit 1: whoops, I see that you wanted numerically next, not the next in the ordered data set. sorry. leave the rest in for historical purposes

    edit 2: replace the my @status... line, so it meets your spec, and remove the unneeded sort when foreaching through the values to find each next value


    Oh, good. Though there were other good answers between the time I started and when I went to write it up, so far, no one used my idea... Assuming there's only one instance of each data value (*), make a reverse mapping from value back to index

    #/usr/bin/perl #pm1216404 use warnings; use strict; use 5.010; # for // #my @status = map {chomp; $_} <DATA>; # use the chomp t +o get rid of the newlines from the DATA right away my @status = sort { $a <=> $b } map {chomp; $_} <DATA>; # edit 2 = sor +t the status after you read and map it, so now my %unmap = map { $status[$_] => $_ } 0..$#status; # makes a hash of +$unmap{value} = index pairs # use Data::Dumper; print Dumper \@status, \%unmap; $|++; foreach my $current ( @status ) { # edit 2 = remove the sort here, be +cause it's sorted above my $idx = $unmap{$current}; my $ni = defined $idx ? $idx+1 : undef; my $next = defined $ni ? $status[$ni] : undef; printf "current=%d, current's index=%s, next idx=%d, next value=%s +\n", $current, $idx//'<undef>', $ni//'<undef>', $next//'<undef>'; } __DATA__ 11673326 11673329 11673325 11673330 11673321 11673335

    *: if there were more than one instance, I think it would find the next after the _last_ instance

Re: get next higher number
by Marshall (Canon) on Jun 11, 2018 at 20:18 UTC
    Is this a homework question? If so, you should tell us that. You will still get help and we will understand how to better help you.

    If this is not a homework question: Where do these numbers come from? What do they represent? How many numbers could there be? What is the maximum range of the numbers? How important is speed of calculation vs storage space? - Yes, that is a complicated question, but this is a common programming tradeoff. Knowing the answers to those basic questions and the overall context of your application would, with a high probability, lead to a more efficient solution and a "better" answer.

    You wrote; "I prefer not to use any other functions or modules." Well, if(looks_like_number($row) calls a function that is not shown in your code.

    Update: I don't see any Print statement in the original code. If there is no output, then any calculations are meaningless.

      "I prefer not to use any other functions or modules."   Yes, this does carry the stink of homework.


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

        Nope its a task I was given at work. `looks_like_number` is already used a function in the code so I can use it, but *prefer not to add additional modules and functions* so I won't need to install them.
        P.S which university course teaches Perl in 2018?
        thank you for your time.
Re: get next higher number
by AnomalousMonk (Archbishop) on Jun 11, 2018 at 19:34 UTC
    Can I do it some how with regex?

    As hippo has alluded, a regex approach would seem far from ideal. Howsoever, the following seems to work, but requires Perl version 5.10+ for the  (?(condition)yes-pattern|no-pattern) construct (which embeds the experimental  (?{ CODE }) feature; see Extended Patterns in perlre):

    c:\@Work\Perl>perl -wMstrict -le "use 5.010; ;; use Test::More 'no_plan'; use Test::NoWarnings; ;; my @values = qw( 11673326 11673329 11673325 11673330 11673321 11673335 ); ;; my $sorted_str = join ' ', sort { $a <=> $b } @values; ;; sub next_higher { local our ($from) = @_; ;; return $1 if $sorted_str =~ m{ \b (\d+) \b (?(?{ $from >= $^N }) (*FAIL)) }xms; ;; return; } ;; is next_higher(11673326), 11673329; is next_higher(11673335), undef; is next_higher(11673321), 11673325; done_testing; " ok 1 ok 2 ok 3 1..3 ok 4 - no warnings 1..4
    I leave further testing to you :)

    (NB: From, I believe, Perl version 5.18 on, the
        local our ($from) = @_;
    statement in the  next_higher() function can be replaced with a more familiar lexical
        my ($from) = @_;
    statement and the regex will work properly; can't test this ATM.)

    Update: When discussing Perl version dependency in the first paragraph, I should also have mentioned that  (*FAIL) was introduced with 5.10 (see Special Backtracking Control Verbs in perlre). However, the effect of  (*FAIL) is exactly duplicated by  (?!) in pre-5.10 regexen, so  (?(condition)yes-pattern|no-pattern) remains the critical problem.


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

Re: get next higher number
by tybalt89 (Monsignor) on Jun 11, 2018 at 22:56 UTC
    #!/usr/bin/perl # https://perlmonks.org/?node_id=1216404 use strict; use warnings; use Search::Dict; my $file = join "", sort <DATA>; open my $handle, '<', \$file or die; for my $argument ( 11673326, 11673335, 11673321 ) { 0 <= look $handle, $argument + 1 or die "look error"; print "next after $argument is ", <$handle> // "undefined\n"; } __DATA__ 11673326 11673329 11673325 11673330 11673321 11673335

    Outputs:

    next after 11673326 is 11673329 next after 11673335 is undefined next after 11673321 is 11673325
Re: get next higher number
by Anonymous Monk on Jun 12, 2018 at 16:08 UTC
    This almost sounds like a homework problem. If the file is unordered, you must read through all of it, first looking for numbers that are bigger than the one you have. You must keep each number and check each subsequent bigger-number to see if it is smaller than the one you already have, while also correctly handling the first-number case. If you can't code that, go ask your teacher.