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

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

I was looking at this expression in perl:

@ranks[@sorted_positions] = (0 .. $#sorted_positions);
Does perl increment the index for both arrays automatically? Is it shorthand for a 'foreach' loop?

Replies are listed 'Best First'.
Re: Loops in Perl
by LanX (Saint) on Mar 11, 2014 at 18:52 UTC
    that's a list assignment to an array slice on the LHS.

    > Is it shorthand for a foreach loop?

    ehm ... well the effect is analogous to many scalar assignments

    $ranks[ $sorted_positions[$_] ] = $_ for 0 .. $#sorted_positions

    but actually it's an assignment of list to list

    > Does perl increment the index for both arrays automatically?

    there is no increment after the range operator created a list on the RHS.

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Loops in Perl
by kcott (Archbishop) on Mar 11, 2014 at 21:12 UTC

    G'day carlriz,

    Let's say @sorted_positions has five elements (1, 3, 5, 7, 9). (So, $#sorted_positions will be 4.)

    The code you posted:

    @ranks[@sorted_positions] = (0 .. $#sorted_positions);

    will be evaluated like this:

    @ranks[1, 3, 5, 7, 9] = (0, 1, 2, 3, 4);

    The element $ranks[1] will be set to 0, the element $ranks[3] will be set to 1 and so on.

    Here's a short piece of code demonstrating this.

    #!/usr/bin/env perl -l use strict; use warnings; my @ranks = qw{a b c d e f g h i j}; my @sorted_positions = (1, 3, 5, 7, 9); print "@ranks"; @ranks[@sorted_positions] = (0 .. $#sorted_positions); print "@ranks";

    Output:

    a b c d e f g h i j a 0 c 1 e 2 g 3 i 4

    The elements with indices 0, 2, 4, 6 and 8 are unchanged. The remaining elements (with indices 1, 3, 5, 7 and 9) are set to the values 0, 1, 2, 3 and 4 respectively.

    -- Ken

      Awesome example. Thanks!

Re: Loops in Perl
by Laurent_R (Canon) on Mar 11, 2014 at 18:57 UTC

    Yes, what you see on the left side of the equal sign is called an array slice. You can do something like this:

    my @array = 0..5; my @ranks[@array] = 5..10; # @ranks now contains 5, 6, 7, 8, 9, 10
    The code you posted is just the same idea.

    Update:

    As noted by AnomalousMonk in the post just below, there is an error in the code above: it should be:
    my @array = 0..5; my @ranks; @ranks[@array] = 5..10; # @ranks now contains 5, 6, 7, 8, 9, 10
    We can't use "my" on an array with subscripts accessing to part of the array, the array must be declared before. I tested the code directly under the debugger, where it is not pôssible to use "my", and added the my on the line afterwards, creating erroneous code.

      my @ranks[@array] = 5..10; is a syntax error.

      c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my @array = 0..5; my @ranks; @ranks[@array] = 5..10; dd \@ranks; " [5 .. 10]

      Update: However, the following works for initializing a new lexical (reverse used just to add some variety):

      c:\@Work\Perl>perl -wMstrict -MData::Dump -le "my @array = reverse 0 .. 5; my @ranks = (5 .. 10)[@array]; dd \@ranks; " [10, 9, 8, 7, 6, 5]
        Thank you for your comment, AnomalousMonk, you are right. I added the "my" after having tested and that was a mistake. I have updated my post accordingly.
Re: Loops in Perl
by Anonymous Monk on Mar 11, 2014 at 20:39 UTC
Re: Loops in Perl
by 2teez (Vicar) on Mar 11, 2014 at 20:46 UTC

    Hi carlriz,
    Also check Slices Just a "kobo" note!

    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
Re: Loops in Perl
by Marshall (Canon) on Mar 14, 2014 at 05:14 UTC
    This is actually a rather strange idea in Perl.
    And I figure syntactically incorrect.

    Normal would be:

    @ranks= sort { my ($A) = (split($a,'')))[0]; my ($B) = split($b,'')))[0]; $A[0] <=> $B[0] }@ranks; #sorts on numeric comparison #between item's [0] in the @ranks array }