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

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

Hi,

I'm trying to re-learn perl after a long hiatus. Trying to reverse the array contents without using the "reverse" function, just to see how much I understand things. Looks like I have totally forgotten everything.

Here is what I am trying:

use warnings; use strict; use diagnostics; my @arr = ('a'..'z'); print "@arr\n"; foreach my $thing (@arr) { my $ele = pop @arr; my $stuff = unshift @arr, $ele; print "$ele "; }

And here is the output:

C:\perlscripts>perl arrays.pl a b c d e f g h i j k l m n o p q r s t u v w x y z z y x w v u t s r q p o n m l k j i h g f e d c b a C:\perlscripts>

What I fail to understand is, iterating the array using $thing variable but not using it at all in the loop still gives the expected result.

Tried printing out the $thing contents, by using print "[$thing]".

use warnings; use strict; use diagnostics; my @arr = ('a'..'z'); print "@arr\n"; foreach my $thing (@arr) { print "[$thing]"; my $ele = pop @arr; my $stuff = unshift @arr, $ele; print "$ele "; }

Here's the output

C:\perlscripts>perl arrays.pl a b c d e f g h i j k l m n o p q r s t u v w x y z [a]z [a]y [a]x [a]w [a]v [a]u [a]t [a]s [a]r [a]q [a]p [a]o [a]n [a]m +[a]l [a]k [a]j [a]i [a]h [a]g [a]f [a]e [a]d [a]c [a]b [a]a C:\perlscripts>

What am I missing? Something very basic and "duh" I guess..

Replies are listed 'Best First'.
Re: Array confusion.
by choroba (Cardinal) on Feb 14, 2017 at 22:01 UTC
    Your code didn't really revert the array. Try printing the array in each step:
    a b c d e f g h i j k l m n o p q r s t u v w x y z z a b c d e f g h i j k l m n o p q r s t u v w x y y z a b c d e f g h i j k l m n o p q r s t u v w x x y z a b c d e f g h i j k l m n o p q r s t u v w w x y z a b c d e f g h i j k l m n o p q r s t u v v w x y z a b c d e f g h i j k l m n o p q r s t u u v w x y z a b c d e f g h i j k l m n o p q r s t t u v w x y z a b c d e f g h i j k l m n o p q r s s t u v w x y z a b c d e f g h i j k l m n o p q r r s t u v w x y z a b c d e f g h i j k l m n o p q q r s t u v w x y z a b c d e f g h i j k l m n o p p q r s t u v w x y z a b c d e f g h i j k l m n o o p q r s t u v w x y z a b c d e f g h i j k l m n n o p q r s t u v w x y z a b c d e f g h i j k l m m n o p q r s t u v w x y z a b c d e f g h i j k l l m n o p q r s t u v w x y z a b c d e f g h i j k k l m n o p q r s t u v w x y z a b c d e f g h i j j k l m n o p q r s t u v w x y z a b c d e f g h i i j k l m n o p q r s t u v w x y z a b c d e f g h h i j k l m n o p q r s t u v w x y z a b c d e f g g h i j k l m n o p q r s t u v w x y z a b c d e f f g h i j k l m n o p q r s t u v w x y z a b c d e e f g h i j k l m n o p q r s t u v w x y z a b c d d e f g h i j k l m n o p q r s t u v w x y z a b c c d e f g h i j k l m n o p q r s t u v w x y z a b b c d e f g h i j k l m n o p q r s t u v w x y z a a b c d e f g h i j k l m n o p q r s t u v w x y z

    So, you rather "rotated" it, and it ended up being exactly the same as in the beginning.

    As to $thing: changing the array you iterate over is dangerous. In this case, check what's the first element in the first iteration, second element in the second iteration, etc. You don't need to use the loop variable, it's there only to guarantee the number of iterations; I'd probably just write

    for (@arr) {

    or, to avoid changing the array being iterated

    for (1 .. @arr) {

    to make it clear the number of steps is what matters.

    ($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: Array confusion.
by Mr. Muskrat (Canon) on Feb 14, 2017 at 22:07 UTC

    You are modifying @arr inside the loop in such a way that $thing containing 'a' at each iteration. It's easier to see with some modifications:

    use warnings; use strict; use diagnostics; my @arr = ('a'..'z'); print "@arr\n"; foreach my $thing (@arr) { print "[$thing]"; my $ele = pop @arr; my $stuff = unshift @arr, $ele; print "$ele - @arr\n"; } __END__ a b c d e f g h i j k l m n o p q r s t u v w x y z [a]z - z a b c d e f g h i j k l m n o p q r s t u v w x y [a]y - y z a b c d e f g h i j k l m n o p q r s t u v w x [a]x - x y z a b c d e f g h i j k l m n o p q r s t u v w [a]w - w x y z a b c d e f g h i j k l m n o p q r s t u v [a]v - v w x y z a b c d e f g h i j k l m n o p q r s t u [a]u - u v w x y z a b c d e f g h i j k l m n o p q r s t [a]t - t u v w x y z a b c d e f g h i j k l m n o p q r s [a]s - s t u v w x y z a b c d e f g h i j k l m n o p q r [a]r - r s t u v w x y z a b c d e f g h i j k l m n o p q [a]q - q r s t u v w x y z a b c d e f g h i j k l m n o p [a]p - p q r s t u v w x y z a b c d e f g h i j k l m n o [a]o - o p q r s t u v w x y z a b c d e f g h i j k l m n [a]n - n o p q r s t u v w x y z a b c d e f g h i j k l m [a]m - m n o p q r s t u v w x y z a b c d e f g h i j k l [a]l - l m n o p q r s t u v w x y z a b c d e f g h i j k [a]k - k l m n o p q r s t u v w x y z a b c d e f g h i j [a]j - j k l m n o p q r s t u v w x y z a b c d e f g h i [a]i - i j k l m n o p q r s t u v w x y z a b c d e f g h [a]h - h i j k l m n o p q r s t u v w x y z a b c d e f g [a]g - g h i j k l m n o p q r s t u v w x y z a b c d e f [a]f - f g h i j k l m n o p q r s t u v w x y z a b c d e [a]e - e f g h i j k l m n o p q r s t u v w x y z a b c d [a]d - d e f g h i j k l m n o p q r s t u v w x y z a b c [a]c - c d e f g h i j k l m n o p q r s t u v w x y z a b [a]b - b c d e f g h i j k l m n o p q r s t u v w x y z a [a]a - a b c d e f g h i j k l m n o p q r s t u v w x y z

    Before the loop @arr contains 'a'..'z' as expected.

    First iteration: $thing ($arr[0]) contains 'a', $ele ($arr[25]) contains 'z' which goes to the beginning and now 'a' is at $arr[1].

    Second iteration: $thing ($arr[1]) contains 'a', $ele ($arr[25]) contains 'y' which goes to the beginning and now 'a' is at $arr[2].

    Wash, rinse, repeat until you've reached $arr[25].

    update: removed meaningless comment

Re: Array confusion.
by Laurent_R (Canon) on Feb 14, 2017 at 22:17 UTC
    Here is an example using the loop variable to populate another array:
    $ perl -e ' my @arr = ('a'..'z'); > my @result; > foreach my $thing (@arr) { > unshift @result, $thing; > } > print "@result"; > ' z y x w v u t s r q p o n m l k j i h g f e d c b a
Re: Array confusion.
by haukex (Archbishop) on Feb 14, 2017 at 22:07 UTC

    Hi anonypl,

    What I fail to understand is, iterating the array using $thing variable but not using it at all in the loop still gives the expected result.

    Regardless of whether you use $thing or not, the loop body will normally still be executed once per element of the array. However, as others have already pointed out, your code may be printing the array in reverse order, but doesn't actually reverse the array itself; also note that Foreach Loops says this:

    If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.

    Alternative ideas:

    my @arr = ('a'..'z'); my @out; push @out, pop @arr for 0..$#arr; print "@out\n"; @arr = ('a'..'z'); @arr[$_,-$_-1] = @arr[-$_-1,$_] for 0..$#arr/2; print "@arr\n";

    Updated in regards to others having already replied in the meantime.

    Update 2: Actually, in my first example above it isn't really necessary to destroy @arr to build @out, in that regard Laurent_R's idea is better than the first example above. The second example is still applicable, though.

    Hope this helps,
    -- Hauke D

Re: Array confusion.
by johngg (Canon) on Feb 14, 2017 at 23:10 UTC

    A non-destructive way map'ing pop inside an anonymous subroutine.

    johngg@shiraz:~/perl > perl -Mstrict -Mwarnings -E ' my @arr = ( q{a} .. q{z} ); my @rev = sub { map { pop } 1 .. @_ }->( @arr ); say qq{@arr}; say qq{@rev};' a b c d e f g h i j k l m n o p q r s t u v w x y z z y x w v u t s r q p o n m l k j i h g f e d c b a

    I hope this is of interest.

    Cheers,

    JohnGG

      A slightly simplified version:

      @a = 'a'..'z';; @b = sub{ map pop, @_ }->( @a );; print @b;; z y x w v u t s r q p o n m l k j i h g f e d c b a

      Or even just:

      @c = map pop @a, @a;; print @c;; z y x w v u t s r q p o n m l k j i h g f e d c b a

      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
      In the absence of evidence, opinion is indistinguishable from prejudice.
        @c = map pop @a, @a;;

        Much simpler, true, but destructive if that is a concern.

        johngg@shiraz:~/perl > perl -Mstrict -Mwarnings -E ' my @arr = ( q{a} .. q{z} ); my @rev = map pop @arr, @arr; say qq{@arr}; say qq{@rev};' z y x w v u t s r q p o n m l k j i h g f e d c b a

        Cheers,

        JohnGG

Re: Array confusion.
by Marshall (Canon) on Feb 15, 2017 at 03:30 UTC
    I think that the problems of modifying a foreach loop array within the loop have been explained well in other posts.

    You said: Trying to reverse the array contents without using the "reverse" function...

    I haven't bench marked this, but making a copy of an array is usually a relatively fast operation. I suspect "rotating" an array by decreasing its size by one item and then increasing its size by one item is more "expensive" than making a copy which is then consumed (size decreases at every access).

    The basic issue with your code is the use of foreach and modifying the array that foreach is iterating over within the loop.

    Consider this simple idea:

    use warnings; use strict; use diagnostics; my @arr = ('a'..'z'); print "@arr\n"; my @copy = @arr; while (@copy) #size of @copy is re-evaluated at each iteration { my $ele = pop @copy; print "$ele "; } print "\n"; __END__ Prints: a b c d e f g h i j k l m n o p q r s t u v w x y z z y x w v u t s r q p o n m l k j i h g f e d c b a
Re: Array confusion.
by anonypl (Novice) on Feb 15, 2017 at 13:30 UTC

    Respected Monks,

    Thank you very much for your replies. Some I am able to understand, some I need to re-read to understand because I am not very well versed with Perl, but I am amazed by the helpful nature of you all. Thank you very much. You all are absolute masters of Perl (and many other languages I guess). At 41 years, I hope in future years I am able to achieve even 20% of your level of mastery.

Re: Array confusion.
by BillKSmith (Monsignor) on Feb 15, 2017 at 03:02 UTC
    Deleted all text. Sorry, My observation is already covered.

    Quote from the "Foreach Loops" section of perlsyn:

    If any part of LIST is an array, foreach will get very confused if you add or remove elements within the loop body, for example with splice. So don't do that.