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

Hi Monks, Can anyone tell me why original array(xx) is changing in below code. Thanks.
#!/usr/bin/perl use warnings; use strict; use diagnostics; use Data::Dumper; sub left_triangle { my @ali=@_; my @aoa2; foreach (1..@ali-1) { pop @{$ali[$_]}; #push @aoa2, $ali[$_]; } return \@ali; } my @xx = ([3],[7,5],[2,4,6],[8,5,9,3]); print Dumper left_triangle(@xx); print Dumper \@xx;

Replies are listed 'Best First'.
Re: Pass by Value does not work
by almut (Canon) on Feb 21, 2009 at 09:42 UTC

    You haven't made a 'deep' copy of your data structure, which means the array references are still pointing to the original data. You've only copied the references themselves...

      merlyn, as is his won't, wrote an extremely descriptive & helpful article on this very topic i.e. the problem you've unwittingly encountered, some while ago in the UNIX Review.

      A user level that continues to overstate my experience :-))

      Not to sound too dense, but doesn't Perl always use "pass by reference," in that changing anything in the $@ array will change its values in the calling routine?

      Of course, the language I grew up using always passes by reference, so it usually takes a mental effort to assume any changes in a sub to passed variables don't propagate upstream.


      Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

        I suppose the OP was thinking that - due to the @ali = @_ - the entire data structure would be copied...  And the "pass by value" was probably referring to left_triangle(@xx) vs. left_triangle(\@xx) — with the "values" being the array refs.   (Only the OP can tell for sure, though, what his/her expectation had been.)

Re: Pass by Value does not work
by duelafn (Parson) on Feb 21, 2009 at 13:33 UTC

    While merlyn's article does cover the issue, the short answer/easy solution is to use Storable's dclone (core module since 5.8.0)

    #!/usr/bin/perl use warnings; use strict; use diagnostics; use Storable qw/dclone/; use Data::Dumper; sub left_triangle { my @ali=map dclone($_), @_; my @aoa2; foreach (1..@ali-1) { pop @{$ali[$_]}; #push @aoa2, $ali[$_]; } return \@ali; } my @xx = ([3],[7,5],[2,4,6],[8,5,9,3]); print Dumper left_triangle(@xx); print Dumper \@xx;

    Good Day,
        Dean

Re: Pass by Value does not work
by velusamy (Monk) on Feb 21, 2009 at 09:53 UTC
    Hi,

    The @xx was having elements as array references. In subroutine you changed @xx values but the values were references so that only your array values were affected.

      Thanks all, I now understand it.
Re: Pass by Value does not work
by Marshall (Canon) on Feb 22, 2009 at 23:00 UTC
    When you call left_triangle with what we call a List of List (LoL), you are passing a list of references to the sub, the reference to list(s) (3), (7,5), (2,4,6), (8,5,9,3). In Perl [] means reference to or address of or pointer to, basically the same idea. my @xx = ([3],[7,5],[2,4,6],[8,5,9,3]); is a list of 4 references to other lists. If you use that pointer to the list, then you are modifying the list itself.

    Perl is MAGIC with lists!
    How do you clone a LoL (List of List)? This is also called a "deep copy".
    Easy:
    my @xx_clone = map {[$_]}@xx;
    Correction: my @xx_clone = map {[@$_]}@xx;
    map is a transformation operator that operates on lists.
    Here a list of references to lists (@xx) goes into the map. Then each list gets expanded via @$_, then enclosed within a new address (the [] operator) and a new list of those is passed to @xx_clone. So we have a new List of Lists! One line!

    To do this in other languages like C, requires a lot of code! And if we are talking about "ragged arrays" like we have here with varying numbers of columns, even more code!

      Don't you mean:

      my @xx_clone = map { [ @{$_} ] } @xx;
      G. Wade
        I think we were both a little wrong..ooops..

         my @xx_clone = map { [ @$_ ] } @xx;

        looks like that's better.. Thanks!!!.. UpdateI corrected post above. Thanks again. In general the {} are only needed when subscripts are involved. Another situation is say: @list = @{listref_returning_fun()};

        In above posts, to get array back, my @lol = @{left_triangle(@xx)}; The above posts return reference to lists of lists. to get the LOL back, deference one time using @X= @{subroutine()} syntax.