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

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

Hi!

I have a sorted array of numbers low=>high and need to test IF one of the values is between $a and $b. And if more than one of the values is betwen $a and $b, need to take the higher.

One hour ago, I began to try, so far nothing positive unless long foreach and IF statements... There is probably a faster method?

Thank you for your help!

Replies are listed 'Best First'.
Re: Array, element inside limits
by RMGir (Prior) on Jun 15, 2009 at 16:07 UTC
    First, don't use $a and $b in your actual code, unless you're in a sort block. They're semi-reserved names.

    Second, it sounds like simply binary searching your array for your upper bound and finding the first value less than that upper bound should get you your answer, as long as it's greater than your lower bound. No?


    Mike
Re: Array, element inside limits
by Transient (Hermit) on Jun 15, 2009 at 16:17 UTC
    my %results = map { $_ => 1 if ( $low_watermark < $_ && $_ < $high_watermark ) } @all_numbers;

    will get all of the matching numbers
    my @ordered_result_list = sort keys %results; print $ordered_result_list[-1], "\n";
    will get the highest.

    Update: As stated correctly below - this is really what you'd want (or similar) -

    my @results = sort { $a <=> $b } grep { $low_watermark < $_ && $_ < $high_watermark } @all_numbers;.

    Thanks jwkrahn and ikegami!
      my %results = map { $_ => 1 if ( $low_watermark < $_ && $_ < $high_watermark ) } @all_numbers;

      will get all of the matching numbers

      Not if any of the numbers are duplicates because you are storing them in hash keys which will remove duplicates.

      Why not just:

      my @results = grep $low_watermark < $_ && $_ < $high_watermark, @all_n +umbers;
        I assumed an instance of the number was sufficient and that duplicates could be ignored, but you're absolutely right. I think grep would be a better call.

      my @ordered_result_list = sort keys %results;

      That's wrong. You're sorting lexically, so 10 comes before 9.

      print sort 1..10; # 1 10 2 3 4 5 6 7 8 9

      The above is equivalent to

      print sort { $a cmp $b } 1..10; # 1 10 2 3 4 5 6 7 8 9

      You want to sort numerically.

      print sort { $a <=> $b } 1..10; # 1 2 3 4 5 6 7 8 9 10

      Of course, there's no reason to sort the result if you just want the highest.

      use List::Util qw( max ); print max 1..10; # 10

      my %results = map { $_ => 1 if ( $low_watermark < $_ && $_ < $high_wat +ermark ) } @all_numbers;

      That's wrong. You're incorrectly assuming the expression in the map returns nothing when the condition is false.

      $ perl -wle'my %h = map { $_ => 1 if $_ > 5; } 1..3; print 0+keys(%h); +' Odd number of elements in hash assignment at -e line 1. 1

      You wanted:

      my %results = map { if ( $low_watermark < $_ && $_ < $high_watermark ) + { $_ => 1 } else { () } } @all_numbers;

      Or:

      my %results = map { ( $low_watermark < $_ && $_ < $high_watermark ) ? +$_ => 1 : () } @all_numbers;

      But why is a hash involved at all?

      my @results = grep { $low_watermark < $_ && $_ < $high_watermark } @al +l_numbers; print max(@results), "\n";
        For that matter, why use max, since the natol44 specified "a sorted array of numbers"?
        print $results[-1],"\n";
        :)

        Mike