sub local variable changes global variable.

sub local variable changes global variable.

am 16.10.2011 01:08:19 von JPH

Hi all,

I am passin a two-dimensional array to a sub and when the sub returns, the original array has changed. Eventually I want to pass the array into a recursive sub, so I want to find a way to circumvent
this behaviour. Notice how my global is "@a" and the sub local is "@b"
- Why is this happening
- How can I properly pass a two dimensional array to a sub, without the array in main changing?

Ideas anyone?

Here is the minimal script to reproduce my finding:
=============
#!/usr/bin/perl

use warnings;
use strict;

sub try {
my @b = @_;
$b[ 0 ][ 1 ] = "7";
return 3;
}

my @a;
$a[ 0 ][ 1 ] = " ";

print "Before: " . $a[ 0 ][ 1 ] . "\n";
try( @a );
print "After: " . $a[ 0 ][ 1 ] . "\n";

exit;
=============
What happens:
Before:
After: 7
=============
What I would expect:
Before:
After: 0
=============

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 01:31:00 von Paul Johnson

On Sun, Oct 16, 2011 at 01:08:19AM +0200, JPH wrote:
> Hi all,
>
> I am passin a two-dimensional array to a sub and when the sub

This is your main problem. Perl doesn't have two-dimensional arrays. What it
does have is arrays of array references which, if you squint, can be used as
two-dimensional arrays. And this is what you have here. Armed with this
knowledge, perhaps you can already see the problem and solution.

> returns, the original array has changed. Eventually I want to pass
> the array into a recursive sub, so I want to find a way to
> circumvent this behaviour. Notice how my global is "@a" and the sub
> local is "@b"
> - Why is this happening
> - How can I properly pass a two dimensional array to a sub, without the array in main changing?
>
> Ideas anyone?

When call try(), (or more accurately, when you assign to @b) you are
performing a shallow copy of the array. It seems that you really need a deep
copy. You could either explicity write this yourself, or you could use
something such as dclone in Storable.

> Here is the minimal script to reproduce my finding:
> =============
> #!/usr/bin/perl
>
> use warnings;
> use strict;
>
> sub try {
> my @b = @_;
> $b[ 0 ][ 1 ] = "7";
> return 3;
> }
>
> my @a;
> $a[ 0 ][ 1 ] = " ";
>
> print "Before: " . $a[ 0 ][ 1 ] . "\n";
> try( @a );
> print "After: " . $a[ 0 ][ 1 ] . "\n";
>
> exit;
> =============
> What happens:
> Before:
> After: 7
> =============
> What I would expect:
> Before:
> After: 0
> =============

--
Paul Johnson - paul@pjcj.net
http://www.pjcj.net

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 01:44:02 von Rob Dixon

On 16/10/2011 00:08, JPH wrote:
> Hi all,
>
> I am passin a two-dimensional array to a sub and when the sub returns,
> the original array has changed. Eventually I want to pass the array into
> a recursive sub, so I want to find a way to circumvent this behaviour.
> Notice how my global is "@a" and the sub local is "@b"
>
> - Why is this happening
> - How can I properly pass a two dimensional array to a sub, without the array in main changing?
>
> Ideas anyone?
>
> Here is the minimal script to reproduce my finding:
> =============
> #!/usr/bin/perl
>
> use warnings;
> use strict;
>
> sub try {
> my @b = @_;
> $b[ 0 ][ 1 ] = "7";
> return 3;
> }
>
> my @a;
> $a[ 0 ][ 1 ] = " ";
>
> print "Before: " . $a[ 0 ][ 1 ] . "\n";
> try( @a );
> print "After: " . $a[ 0 ][ 1 ] . "\n";
>
> exit;
> =============
> What happens:
> Before:
> After: 7
> =============
> What I would expect:
> Before:
> After: 0
> =============

Naming your variables just a and b is a bad idea - the name should
reflect what they contain. Also it is generally better to pass arrays
and hashes to subroutines by reference, but I will work with what you
have written so far as, depending on the problem you are solving, any
changes may be irrelevant.

Your "two-dimensional array" @a is an array of array references, so
calling the subroutine as

try(@a);

in fact passes each row as a separate parameter, like this

try($row0, $row1, $row2, $row3);

and since your subroutine starts with

sub try {
my @b = @_;
:
}

you are simply copying those array references into @b, like this

my @b = ($row0, $row1, $row2, $row3);

so $a[0][1] is the same as $row0->[1] and so also $b[0][1], so by
changing one you are also changing the other. For the local array to be
independent of the one passed, you must duplicate all the rows,
replacing the first line of the subroutine with something like

sub try {
my @b;
foreach my $row (@_) {
push @b, [@$row];
}
:
}


I think it is unlikely that your design is the best solution to your
problem, but since you say nothing of your requirements I cannot suggest
anything better. Depending on the size of your arrays and the level of
recursion you may easily absorb large amounts of memory.

I hope this helps,

Rob

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 04:05:08 von Shawn H Corey

On 11-10-15 07:44 PM, Rob Dixon wrote:
>
> sub try {
> my @b;
> foreach my $row (@_) {
> push @b, [@$row];
> }
> :
> }
>

Or you could use dclone() from Storable:

use Storable qw( dclone );

sub try {
my @b = @{ dclone( \@_ ) };
...
}

Storable is a standard module that is installed with Perl. For a list of
all standard pragmatics and modules, see `perldoc perlmodlib`.


--
Just my 0.00000002 million dollars worth,
Shawn

Confusion is the first step of understanding.

Programming is as much about organization and communication
as it is about coding.

The secret to great software: Fail early & often.

Eliminate software piracy: use only FLOSS.

"Make something worthwhile." -- Dear Hunter

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 08:29:24 von jwkrahn

JPH wrote:
> Hi all,

Hello,

> I am passin a two-dimensional array to a sub and when the sub returns,
> the original array has changed. Eventually I want to pass the array into
> a recursive sub, so I want to find a way to circumvent this behaviour.
> Notice how my global is "@a" and the sub local is "@b"
> - Why is this happening
> - How can I properly pass a two dimensional array to a sub, without the
> array in main changing?
>
> Ideas anyone?

Change your subroutine so that it does not modify the array values?


> Here is the minimal script to reproduce my finding:
> =============
> #!/usr/bin/perl
>
> use warnings;
> use strict;
>
> sub try {
> my @b = @_;
> $b[ 0 ][ 1 ] = "7";
> return 3;
> }
>
> my @a;
> $a[ 0 ][ 1 ] = " ";

Or more simply:

my @a = [ undef, " " ];


> print "Before: " . $a[ 0 ][ 1 ] . "\n";
> try( @a );
> print "After: " . $a[ 0 ][ 1 ] . "\n";
>
> exit;
> =============
> What happens:
> Before:
> After: 7
> =============
> What I would expect:
> Before:
> After: 0

If you originally assign " " to $a[ 0 ][ 1 ] why would you now expect it
to contain 0?



John
--
Any intelligent fool can make things bigger and
more complex... It takes a touch of genius -
and a lot of courage to move in the opposite
direction. -- Albert Einstein

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 11:31:37 von JPH

You got me there, John. Indeed I do expect it to contain ' ' and not 0 like I stated.
> If you originally assign " " to $a[ 0 ][ 1 ] why would you now expect it to contain 0?
>
>
>
> John

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 16.10.2011 20:10:06 von JPH

Thanks! My script seems to work with dclone.

Though the (deep) recursion takes a lot of time ...
What is your opinion, would it pay off (in performance) when I rewrite the script in such a way that I do not use dclone, but string manipulation routines instead? So:
- passing a single dimension array containing 16 elements with 16-character strings and doing substr()
- instead of passing a 2 dimensional (16x16) array passing 1-character strings and doing dclone
Every pass I only change a single character, then I run some tests and so on.

On 10/16/2011 04:05 AM, Shawn H Corey wrote:
> On 11-10-15 07:44 PM, Rob Dixon wrote:
>>
>> sub try {
>> my @b;
>> foreach my $row (@_) {
>> push @b, [@$row];
>> }
>> :
>> }
>>
>
> Or you could use dclone() from Storable:
>
> use Storable qw( dclone );
>
> sub try {
> my @b = @{ dclone( \@_ ) };
> ...
> }
>
> Storable is a standard module that is installed with Perl. For a list of all standard pragmatics and modules, see `perldoc perlmodlib`.
>
>

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 17.10.2011 00:18:17 von Rob Dixon

On 16/10/2011 19:10, JPH wrote:
>
> On 10/16/2011 04:05 AM, Shawn H Corey wrote:
>> On 11-10-15 07:44 PM, Rob Dixon wrote:
>>>
>>> sub try {
>>> my @b;
>>> foreach my $row (@_) {
>>> push @b, [@$row];
>>> }
>>> :
>>> }
>>>
>>
>> Or you could use dclone() from Storable:
>>
>> use Storable qw( dclone );
>>
>> sub try {
>> my @b = @{ dclone( \@_ ) };
>> ...
>> }
>>
>> Storable is a standard module that is installed with Perl. For a list
>> of all standard pragmatics and modules, see `perldoc perlmodlib`.
>
> Thanks! My script seems to work with dclone.
>
> Though the (deep) recursion takes a lot of time ...
> What is your opinion, would it pay off (in performance) when I rewrite
> the script in such a way that I do not use dclone, but string
> manipulation routines instead? So:
> - passing a single dimension array containing 16 elements with
> 16-character strings and doing substr()
> - instead of passing a 2 dimensional (16x16) array passing 1-character
> strings and doing dclone
> Every pass I only change a single character, then I run some tests and
> so on.

I suggest you try a simplified version of my code, which is bound to be
faster than calling dclone:

sub try {
my @b = map [@$_], @_;
:
}

Whether it is fast enough only you can tell.

I would also suggest an intermediate solution, where you have a
sixteen-element array, each of sixteen characters. Rather than using
substr you could look at unpack, or possibly split and join.

It is hard to help you further without knowing more about the problem
you are solving.

Rob

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 17.10.2011 00:42:26 von Rob Dixon

On 16/10/2011 23:18, Rob Dixon wrote:
> On 16/10/2011 19:10, JPH wrote:
>>
>> Every pass I only change a single character, then I run some tests and
>> so on.

I wonder if it is possible to write your subroutine by using a single
global array and remembering the change so that you can undo it before
the code exits?

my @data = (...);

sub try {
my $old = $data[0][1];
$data[0][1] = '7';
...
$data[0][1] = $old;
return 3;
}

Only remembering a single scalar will be many times more efficient than
remembering the entire 256 scalars in the array.

Rob

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/

Re: sub local variable changes global variable.

am 17.10.2011 20:31:16 von Rob Dixon

On 16/10/2011 00:08, JPH wrote:
>
> I am passin a two-dimensional array to a sub and when the sub returns,
> the original array has changed. Eventually I want to pass the array into
> a recursive sub, so I want to find a way to circumvent this behaviour.
> Notice how my global is "@a" and the sub local is "@b"
> - Why is this happening
> - How can I properly pass a two dimensional array to a sub, without the
> array in main changing?
>
> Ideas anyone?

Your problem fascinates me. Please would you help us to understand the
purpose of your program?

Rob

--
To unsubscribe, e-mail: beginners-unsubscribe@perl.org
For additional commands, e-mail: beginners-help@perl.org
http://learn.perl.org/