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

Hey monks! sorry, this is a bit simple.. trying to calculate the highest number in an array, why doesn't this work???
for ($i = 1; $i < @numbers; $i++) { if ($numbers[$i] > $numbers[$i + 1]) { $highest = $numbers[$i]; } } print $highest;

Replies are listed 'Best First'.
Re: finding highest number
by BrowserUk (Patriarch) on Dec 05, 2002 at 12:58 UTC

    Try adding a couple of print statements to your program and see what it is doing.

    #! perl -slw use strict; my @numbers = (3, 2, 1); my $highest; ## NOTE Perl arrays start from 0 (zero) not 1 (one). for (my $i = 0; $i < @numbers; $i++) { print "Comparing $numbers[$i] with $numbers[$i+1]"; if ($numbers[$i] > $numbers[$i + 1]) { print "Setting \$highest to $numbers[$i]"; $highest = $numbers[$i]; } } print $highest; __END__

    prints

    C:\test>217736 Comparing 3 with 2 Setting $highest to 3 Comparing 2 with 1 Setting $highest to 2 Use of uninitialized value in concatenation (.) or string at C:\test\2 +17736.pl line 8. Comparing 1 with Use of uninitialized value in numeric gt (>) at C:\test\217736.pl line + 9. Setting $highest to 1 1

    Does that make it any clearer for you?


    Okay you lot, get your wings on the left, halos on the right. It's one size fits all, and "No!", you can't have a different color.
    Pick up your cloud down the end and "Yes" if you get allocated a grey one they are a bit damp under foot, but someone has to get them.
    Get used to the wings fast cos its an 8 hour day...unless the Govenor calls for a cyclone or hurricane, in which case 16 hour shifts are mandatory.
    Just be grateful that you arrived just as the tornado season finished. Them buggers are real work.

Re: (nrd) finding highest number
by newrisedesigns (Curate) on Dec 05, 2002 at 12:53 UTC

    Why do something C-style when you can speak simple Perlish?

    my @sorted = sort {$b <=> $a} @numbers; my $highest = $sorted[0];

    This sorts your array from highest to lowest and puts it in @sorted.

    Update: changed "my $highest = $numbers[0];" to "my $highest = $sorted[0];" Many thanks to UnderMine for pointing this out.

    John J Reiser
    newrisedesigns.com

      This may be Perlish, but it is also rather inefficient: O(n*log(n)) vs. O(n).

      If you want to do it the Perlish way, why not use max that can be found in the wonderful List::Util module?

      Just my 2 cents, -gjb-

        Oddly enough, it's more efficient to use sort rather than an explicit Perl loop for for up to a dozen or two elements, if I recall the benchmarks.

        Of course, List::Util has a max function, which would be faster still.

        But don't be so quick to optimize... you may be unoptimizing.

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

      my $highest =(sort {$b <=> $a} @numbers)[0];
      This is a bit more compact

      Hope it helps
      UnderMine

      thanks but i need to keep the order of the array intact for the next stage. ;-)

        You still have your original list in @numbers: sort copies, it doesn't change the original list.

        Hope this helps, -gjb-

Re: finding highest number
by LTjake (Prior) on Dec 05, 2002 at 12:56 UTC
    It's because you're only comparing the current and next numbers in the array. The way I would do it, is keep track of the highest number in another variable. The variable is initialized to the first number in the array.
    use strict; my @array = (1, 5, 4, 10, 20, 2, 1, 3, 7); my $highest = $array[0]; foreach (@array) { $highest = $_ if $_ > $highest; } print $highest;
    Which gives me 20. I'm sure there are prettier ways, but this seems to work.

    Update: and of course, the perlish way would be to use sort. duh. ++newrisedesigns. I need a coffee.

    --
    "I don't intend for this to take on a political tone. I'm just here for the drugs." --Nancy Reagan

      No... that would be known as the slow way. This is an operation which should only have to traverse the array *once* where sort() has higher overhead (perhaps gjb has it right as O(n*log(n)) versus O(n)).

      The better answer is to use something akin to what you posted. I'd want to check for undefined values as well. For an answer I just copied right from a toy mapping app I just coded up last night.

      sub max { my $m; for (@_) { next unless defined; $m = $_, next unless defined $m; $m = $_, next if $m < $_; } $m }
      __SIG__ use B; printf "You are here %08x\n", unpack "L!", unpack "P4", pack "L!", B::svref_2object(sub{})->OUTSIDE;
      thanks LTjake this is cool, but how could you adapt it to calculate second and third highest numbers also??
            how could you adapt it to calculate second and third highest numbers?

        something like:
        use strict; my @array = (1, 5, 4, 10, 20, 2, 1, 3, 7); my @hi = (0) x 3; # '0' assumes positive #'s foreach (@array) { $_ <= $hi[0] and next; @hi = (sort $a<=>$b, @hi, $_)[-3..-1]; } print "@hi";
        (untested)   (and, as said, of course the sort method would be better, at least for small arrays, and would be even more better for finding the top three values:  (sort ..., @array)[-3..-1] )

          p
Re: finding highest number
by thinker (Parson) on Dec 05, 2002 at 13:03 UTC
    Hi,

    There is a flaw in your logic. A corrected version is here.
    Notice how the List::Util::max function does what you want.
    #!/usr/bin/perl -w use strict; use List::Util; #for the max function my @numbers = qw( 10 4 7 2 5 8 1 3 11); my $highest=$numbers[0]; # Initialise this for case of all negatives for (my $i = 1; $i < @numbers; $i++) { if ($numbers[$i] > $highest) { #compare to the previous hig +hest $highest = $numbers[$i]; } } print "\$highest is $highest\n"; #now use List::Util::max print "Max is ", List::Util::max(@numbers),"\n";
    hope this helps

    thinker
Re: finding highest number
by RMGir (Prior) on Dec 05, 2002 at 12:59 UTC
    (I smell homework, maybe... Oh well.)

    There are a couple of problems with your code.

    By looking at $numbers[$i+1], you're going to be reading past the end of the array... Not really a serious problem in perl, but something to keep in mind. You're also skipping element 0 in the array.

    You'll also mark as highest any value that's followed by a lower number. So if @numbers is (1,0,100,0,2,0), you'll get 2 instead of 100.

    You should be comparing $numbers[$i] with the highest value you've seen so far.

    # we assume numbers was set up somewhere above... my $highest; # we'll get undef if @numbers is empty $highest=$numbers[0] if @numbers; for(my $i=1; $i<@numbers; $i++) { if($numbers[$i]>$highest) { $highest=$numbers[$i]; } } print $highest;

    --
    Mike
Re: finding highest number
by FamousLongAgo (Friar) on Dec 05, 2002 at 12:59 UTC

    For one thing, you are ignoring the first element of the array ( Perl arrays are zero-based ), and comparing phantom elements at the end of the array. For example, if you have this array:
    @stuff = ( 90, 5, 4 );
    Your code will start with the second element, and compare the last element to a nonexistent ( and therefore undefined ) value ( $stuff[3] ).
    For another thing, you are resetting the value of $highest every time a following element has a lower value. With the code as it now exists, $highest will always be the last element in the array. I urge you to work this out by hand with the short example.
    Here is something that does what you want:
    my $highest = 0; foreach my $element ( @numbers ) { $highest = $element if $element > $highest; } print $highest;
Re: finding highest number
by gjb (Vicar) on Dec 05, 2002 at 12:57 UTC

    Simply compare $numbers[$i] with $highest rather than $numbers[$i+1], initializing $highest = $numbers[0] (outside of the for).

    Hope this helps, -gjb-

Re: finding highest number
by Chief of Chaos (Friar) on Dec 05, 2002 at 13:02 UTC
    If you don't want to use sort, try this :
    if (scalar(@numbers) > 0) { # compare only when array has entries my $highest = $numbers[0]; # set startvalue foreach my $compNum (@numbers){ # compare all numbers in array $highest = $compNum if ($compNum > $highest); } } else { # nothing to compare }
    ps: code is not tested