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

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

I can sort an array passed into a subroutine, but I can't reverse sort or use a custom sort on that array.

I can't get subroutines to do much of anything with passed arrays. This is one of sevearl array-subroutine tests that I've tried over the last two days. I have tried variations with passing array references, but to no avail.

# test subroutines with an array $var = 8; push( @TaR, $var ); $var = 2; push( @TaR, $var ); $var = 5; push( @TaR, $var ); $var = 4; push( @TaR, $var ); $var = 5; push( @TaR, $var ); $var = 6; push( @TaR, $var ); $var = 1; push( @TaR, $var ); $var = 8; push( @TaR, $var ); print STDOUT "\n\n"; print STDOUT "\n\nAs-is "; # This segment works for $OneLine( @TaR ) { print STDOUT " $OneLine"; } print STDOUT " Done\n\n"; SortedArray( @TaR ); ReversedArray( @TaR ); print STDOUT "\n\n"; # This sort works sub SortedArray { my @PassedArray = @_; my @SortedArray = sort @PassedArray; print STDOUT "\n\nSorted "; for $SubLine( @SortedArray ) { print STDOUT " $SubLine"; } print STDOUT " Done\n\n"; return 1; } # This sort, but doesn't reverse the sort sub ReversedArray { my @PassedArray = @_; my @SortedArray = sort reverse @PassedArray; print STDOUT "\n\nRvrsd "; for $SubLine( @SortedArray ) { print STDOUT " $SubLine"; } print STDOUT " Done\n\n"; return 1; }

Replies are listed 'Best First'.
Re: reverse sort arrays in subroutines
by davido (Cardinal) on May 21, 2013 at 21:39 UTC

    Are you trying to sort in descending order?

    If so, you have a couple of choices. One, you can use a custom sort routine, eg:

    my @SortedArray = sort { $b cmp $a } @PassedArray;

    ...or two, you can reverse the output of sort:

    my @SortedArray = reverse sort @PassedArray;

    The point I'm making is that reverse will act upon the sorted list. The way you have it, sort is acting upon a reversed list, which is incorrect. I hope I'm not missing your point. :) If that's not what you're asking, please clarify.

    By the way, this isn't C++98 (vector.push_back()), you can populate your @TaR array like this: @TaR = ( 8, 2, 5, 4, 5, 6, 1, 8 ); ... and STDOUT is the default for print, so you don't usually need to explicitly mention it.


    Dave

Re: reverse sort arrays in subroutines
by choroba (Cardinal) on May 21, 2013 at 21:47 UTC
    It is a misunderstanding. reverse is not a parameter to sort, it is a function that reverses a list.

    Therefore,

    sort reverse @array
    is in fact
    sort(reverse(@array))
    i.e. it reverses the array and then sorts the result.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

      davido, in my sample code, I'm trying to reverse the sort. I think I have your example but it only does the sort.

      : my @SortedArray = reverse sort @PassedArray;.

      in my sample code

      my @SortedArray = sort reverse @PassedArray;

      choroba, I tried your syntax in my code, but I still get only a sort - not a reverse sort. I'm sure this is part of my overall difficulties with arrays in subroutines, but I'm stymied. Here's what I tried:

      my @SortedArray = sort ( reverse ( @PassedArray ) );
        I was probably not explicit enough. Let's take an example:
        1 3 2
        If we first reverse it, we get
        2 3 1
        and then we sort it to get
        1 2 3

        On the other hand, if we sort it first, we get

        1 2 3
        and we can then reverse it to
        3 2 1

        Functions are not called left to right, but from the innermost to the outermost, i.e. rather right to left.

        لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ

        choroba, I tried your syntax in my code, but I still get only a sort - not a reverse sort. I'm sure this is part of my overall difficulties with arrays in subroutines, but I'm stymied. Here's what I tried: my @SortedArray = sort ( reverse ( @PassedArray ) );

        You misunderstood Choroba's point.

        Choroba was telling you that your syntax (sort reverse ...) is equivalent to sort (reverse (...)), and that, when you do that, you are first reversing the order of the array and then sorting it (so that, in fact, first reversing is is totally useless).

        What you want to do is the other way around: you want to sort the array and then reverse the order of the array thus sorted, which should be done, as others have already pointed with the following syntax:

        my @sorted_array = reverse sort @unsorted_array;

        which is first sorting the array and then reversing the order of the elements (think of the data as moving right to left, from @unsorted_array, through sort, then through reverse, into @sorted_array).

        EDIT: Choroba answered while I was typing. I guess it is clear by now.

Re: reverse sort arrays in subroutines
by AnomalousMonk (Archbishop) on May 22, 2013 at 00:59 UTC

    JockoHelios: your reply indicates you have grasped the points others are making. However, I see a potential stumbling block looming before you.

    The default ordering of sort is lexicographic-ascending (see Alphabetical order and Lexicographical order as potential starting points for info on this), and sort implicitly converts its arguments to strings for this sort and effectively compares elements with the cmp stringwise-comparison operator; see Equality Operators in perlop. However, the way you are initializing your  @TaR array suggests you are dealing with numbers rather than strings.

    If you do intend to work with numbers, be aware that lexicographic sorting can produce very surprising results. Note in the example below that 10 sorts before 2 and 20 before 4 in the default sort. The solution is to supply an explicit numeric-comparison block to sort. (The <=> 'spaceship' numeric-comparison operator is also discussed in Equality Operators along with cmp.) I hope this helps.

    >perl -wMstrict -le "my @TaR = (8, 2, 5, 20, 4, 10, 5, 6, 1, 8); print qq{original: @TaR}; ;; my @sorted_lex = sort @TaR; print qq{lexical: @sorted_lex}; ;; my @sorted_num = sort { $a <=> $b } @TaR; print qq{numeric: @sorted_num}; " original: 8 2 5 20 4 10 5 6 1 8 lexical: 1 10 2 20 4 5 5 6 8 8 numeric: 1 2 4 5 5 6 8 8 10 20

      Thanks to all who posted for your advice and counsel. It's gotten me past the current roadblock, and then some. Several posts were sort of prescient with regard to where I was going next, so when I checked back I had some answers I didn't know I needed. This morning, I found out I needed them :)

      Thanks again for your help.
Re: reverse sort arrays in subroutines
by 2teez (Vicar) on May 21, 2013 at 21:48 UTC

    change the line my @SortedArray = sort reverse @PassedArray; to my @SortedArray = reverse sort @PassedArray; in this case, It should do.
    Please also use strict and warnings, in your codes, you would be glad you do.

    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me

      2teez, That did the trick for the reverse sort. Now I'm back to more involved (for a newbie) subroutine coding, so I'll likely be back with another question before long :)