Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Array value changing for some reason

by Silt (Novice)
on Dec 31, 2018 at 22:18 UTC ( #1227848=perlquestion: print w/replies, xml ) Need Help??

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

I found a problem where I get a different result when printing an array after calling a function, that shouldn't be able to change the array. I got the issue down to this:
use strict; use warnings; main(); sub main { problem([[0,1,2],[3,4,5],[6,7,8]]); } sub problem { print3DArray("\@_",@_);#Output: 0 1 2 3 4 5 6 7 8 reverseArray(@_); print3DArray("\@_",@_);#Output: 6 7 8 3 4 5 0 1 2 } sub reverseArray { for(my $i=0;$i<scalar(@_);$i++){ @{$_[$i]} = reverse @{$_[$i]}; } } sub print3DArray { print shift @_, ":\n"; for(my $i=0;$i<scalar(@_);$i++){ print2DArray(@{$_[$i]}); } } sub print2DArray { for(my $i=0;$i<scalar(@_);$i++){ for(my $j=0;$j<scalar(@{$_[$i]});$j++){ print $_[$i][$j]," "; } print "\n"; } print "\n"; }
@_ should stay the same, since I didn't return anything. I would appreciate any help! Thanks!

Replies are listed 'Best First'.
Re: Array value changing for some reason
by choroba (Archbishop) on Dec 31, 2018 at 22:42 UTC
    return doesn't change @_.

    @_ is an array of aliases to the actual arguments, so changing @_ changes the actual arguments.

    sub { ++$_ for @_ }->(my @arr = 'a' .. 'c'); print "@arr"; # Output: b c d

    In your case, it's bit more complex. Changing the function to

    sub reverseArray { my @arr = @_; for my $i (0 .. $#arr) { @{ $arr[$i] } = reverse @{ $arr[$i] }; } }
    wouldn't have helped, as you're working with an array of arrays. The inner arrays are represented as references, so even if you store the references in a different @arr, they still refer to the same inner arrays.

    To keep the array unchanged, only assign to the copy, don't change the references from the original array.

    sub reverseArray { my @arr = @_; for my $i (0 .. $#arr) { $arr[$i] = [ reverse @{ $arr[$i] } ]; } }
    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
      Thanks, that helped! Even tough I don't understand what "for my $i (0 .. $#arr)" means, changing
      sub reverseArray { for(my $i=0;$i<scalar(@_);$i++){ @{$_[$i]} = reverse @{$_[$i]}; } }
      to
      sub reverseArray { my @arr = @_; for(my $i=0;$i<scalar(@arr);$i++){ $arr[$i] = [reverse @{$arr[$i]}]; } }
      did the trick!

      So, if I understand it correctly, a subroutine can change variables from the function it was called from.

        Even though I don't understand what "for my $i (0 .. $#arr)"

        $#array is the index of the last element of @array. Which is the same as the scalar(@array)-1;

        a subroutine can change variables from the function it was called from

        Yes, if you modify @_, it changes for the caller.

        ... I don't understand what "for my $i (0 .. $#arr)" means ...

        The other point to remember about this type of loop (a so-called Perl-style for-loop) is that is "topicalizes" (see "topic" in perlglossary)  $i or whatever variable you specify, or implicitly  $_ if you don't explicitly specify any variable, to each element of the loop list; see Foreach Loops in perlsyn. Also note that foreach and for are exactly synonymous and completely interchangeable keywords in Perl; the differing behavior of Perl- and C-style for/foreach-loops is determined by the syntax of the loop list expression.


        Give a man a fish:  <%-{-{-{-<

Re: Array value changing for some reason
by kschwab (Vicar) on Dec 31, 2018 at 22:44 UTC
    You don't have to return anything to change an array.
    #!/usr/bin/perl sub plus { for (@_) {$_++} } my @foo=(1,2,3,4); plus(@foo); for(@foo) {print "$_\n"};
    Prints out:
    2
    3
    4
    5
    
Re: Array value changing for some reason
by AnomalousMonk (Bishop) on Jan 01, 2019 at 00:26 UTC

    Note also that with aliasing of the elements of the special  @_ subroutine arguments array (see perlvar), it's "turtles all the way down:"

    c:\@Work\Perl\monks>perl -wMstrict -MData::Dump -le "sub going_down { sublevel_1(@_); } sub sublevel_1 { sublevel_2(@_); } sub sublevel_2 { sublevel_3(@_); } sub sublevel_3 { $_[-1] = 'foo'; } ;; my @ra = qw(a b c d); dd \@ra; ;; going_down(@ra); dd \@ra; " ["a", "b", "c", "d"] ["a", "b", "c", "foo"]


    Give a man a fish:  <%-{-{-{-<

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1227848]
Approved by marto
Front-paged by haukex
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (1)
As of 2021-10-16 19:20 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    My first memorable Perl project was:







    Results (69 votes). Check out past polls.

    Notices?