CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
2d array in sub

 



perldeveloer
New User

Sep 25, 2013, 12:14 PM

Post #1 of 10 (2172 views)
2d array in sub Can't Post

Hi all,

I have a rather simple question.
I want to pass a 2d array to a sub routine.
There I want to modify the 2d array (add elements in row, push new rows to it...).
It seems that all modifications I do with the 2d array in the sub routine are not passed back. But the call of the sub is done via a reference of the 2d array?

What is wrong here?

Thanks in advance!


BillKSmith
Veteran

Sep 25, 2013, 1:30 PM

Post #2 of 10 (2169 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

It is possible to choose the scope of your array such that it can be modified in the subroutine without passing an argument at all. This is generally a very bad practice becuse such code can be very hard to debug. Passing a rererence to the array and using that reference to modify the array is not much better. It is usually a better design to make a copy of the array in the subroutine. Modify the copy and return (with a return statement) a reference to the modified copy. Perl's reference counting ensures that the array continues to exist even after it has gone out of scope.

You seem to have chosen the second option. Passing the reference is the same as passing any other scalar. Once you have the reference in the subroutine, it is no different from the same reference in the main program. I suspect that you do not know how to dereference the reference. Refer to the section 'array-of-arrays' in perldoc perldsc.

Note: Strictly speaking, perl does not have a 2d array. An array-of-arrays serves the function so well that we are seldom aware of the difference.
Good Luck,
Bill


Laurent_R
Veteran / Moderator

Sep 25, 2013, 2:43 PM

Post #3 of 10 (2167 views)
Re: [BillKSmith] 2d array in sub [In reply to] Can't Post

It seems to me, if I understood correctly, that the OP wants to pass two arrays to the routine. And that simply does not work, the two arrays get flattened into one single larger array. The only solution is to pass two array refs (or possibly one array ref and then one array), and I do not think that passing array refs to a subroutine is so bad (yes, the array is often sort of a global variable in such cases, but the fact that you are passing refs to it and using refs in your routine eliminates most of the drawbacks of simple global variable, and you can always create block scope to make the array a local lexical variable). Anyway, there is often no other solution when you want to deal with more than one array, and many standard modules just do that.

Just a small example so sum up: suppose you want to produce an array in which each element is the sum of the respective elements of two input arrays. You might do something like this (quick untested code typed directly under the Internet browser):


Code
my $array1 = qw/ 1 2 3/; 
my $array2 = qw /6 7 8/;
my @sum_array = ar_sum(\$array1, \$array2); # passing array refs to the routine

sub ar_sum {
my ($a1_ref, $a2_ref) = @_; # retrieving the array refs
my @result;
push @result, $a1_ref->[$_] + $a2_ref->[$_] foreach 0..2; # assumes you know for sure both arrays have 3 elements
return @result;
}


Of course, it is possible to hide the truth by making the two arrays part of a larger structure (say, an array of arrays or a hash of arrays), but this is really a cosmetic change or, at best, syntactic sugar, but that does not really change the underlying process.

Hmm, well, currying the function might start to really make a qualitative change, but that's getting into relatively advanced topics not really needed for such a simple problem. No reason to make things complicated when they can be simple, IMHO.


perldeveloer
New User

Sep 26, 2013, 3:00 AM

Post #4 of 10 (2154 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

Thank you for the prompt replies.

But my problem are 2d arrays, not the combination of arrays.

See the following simple code:

[my @array_2d = ();

my @row1 = [0,1,2];
my @row2 = [3,4,5];
my @row3 = [6,7,8];

push @array_2d, @row1;
push @array_2d, @row2;
push @array_2d, @row3;

displayarray(\@array_2d);

sub displayarray
{
my @array = @{$_[0]};

print "size: $#array\n";
for(my $i = 0; $i <= $#array; $i++){
print "row: $i, size: $#{$array[$i]}, ";
for(my $j = 0; $j <= $#{$array[$i]} ; $j++){
print "$array[$i][$j] ";
}
print "\n";
}
}]

A "2d array" is created and displayed in a sub.
But now I want to modify the 2d array in the sub, for example add another row. How can this be realized?

Or should I never use these 2d arrays?

Thanks in advance!


BillKSmith
Veteran

Sep 26, 2013, 6:51 AM

Post #5 of 10 (2148 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

You do not have a 2d array. The array which you call @array_2d contains the three rows combined into a long 1d array. It should contain references to your rows.

Code
push @array_2d, \@row1;  
push @array_2d, \@row2;
push @array_2d, \@row3;


You have other errors. Always use strict and warnings. Fix everything they tell you about.

Note: The array @array is a copy of @array_2d. Changing it will not change @array_2d.

As I said in my previous post, it is possible to do what you ask. I am not showing you how because it is usually a poor design to allow a subroutine to modify variables in a main program. Please explain why you want to do this. If you have a good reason, I will either propose a better design or show you how to implement yours.
Good Luck,
Bill


FishMonger
Veteran / Moderator

Sep 26, 2013, 7:25 AM

Post #6 of 10 (2143 views)
Re: [BillKSmith] 2d array in sub [In reply to] Can't Post

Another problem is that the array is being copied inside the sub, so any changes made to that new array are not reflected in the original array.

Solution is to not copy the array.

Code
use Data::Dumper; 

my @row1 = [0,1,2];
my @row2 = [3,4,5];
my @row3 = [6,7,8];

push @array_2d, @row1;
push @array_2d, @row2;
push @array_2d, @row3;

print "prior to sub\n", Dumper(\@array_2d), "\n";

displayarray(\@array_2d);
print "after sub\n", Dumper(\@array_2d);

sub displayarray
{
my $array = shift;
push $array, [3,2,1];

#print "size: $#array\n";
#for my $i (0..$#array){
# print "row: $i, size: $#{$array[$i]}, ";
# for my $j (0..$#{$array[$i]}){
# print "$array[$i][$j] ";
# }
# print "\n";
#}
}


On a side note, I'd use perl's for loop syntax instead of the C style syntax because it's cleaner and more efficient.


(This post was edited by FishMonger on Sep 26, 2013, 7:26 AM)


2teez
Novice

Sep 26, 2013, 7:37 AM

Post #7 of 10 (2140 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

The following is an improvement on your code:

Code
use warnings; 
use strict;
use Data::Dumper;

my @array_2d;

my @row1 = [0,1,2];
my @row2 = [3,4,5];
my @row3 = [6,7,8];

push @array_2d, @row1, @row2, @row3; # see this

displayarray(@array_2d);

sub displayarray
{
my @array = @_;

print 'size: ',scalar $#array,$/;

my @new_row;

for (0..$#array){
my $sum = 0;
print "row: ",$_,', size: ',$#{$array[$_]},', array_values: ',
join " " =>@{$array[$_]},$/;

$sum+=$_ for @{$array[$_]}; # add all the element in a row

push @new_row,$sum;
}

push @array,[@new_row]; # add another row

print Dumper \@array; # check the array now
}

I will also suggest you check "perldsc"
Hope this help.


FishMonger
Veteran / Moderator

Sep 26, 2013, 7:38 AM

Post #8 of 10 (2140 views)
Re: [FishMonger] 2d array in sub [In reply to] Can't Post

I edited out a portion of my prior post that I should have left in.

Sorry Bill but you're mistaken. The OP's @array_2d array is a 2d array because the @row1, @row3, and @row3 arrays each have a single element which is an array ref. If the OP had used parens instead of the brackets, then your example of pushing the array refs would be needed and would end up with the same resulting 2d array.


Laurent_R
Veteran / Moderator

Sep 27, 2013, 3:46 PM

Post #9 of 10 (2119 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

Hi,

this code will possibly do something, but it is probably wrong:


Code
my @row1 = [0,1,2]; 
my @row2 = [3,4,5];
my @row3 = [6,7,8];

push @array_2d, @row1;
push @array_2d, @row2;
push @array_2d, @row3;


You probably want to build a simple array of arrays (AoA), i.e. something like this:

Code
my $row1 = [0,1,2]; #  $row1 is a ref to an array 
my $row2 = [3,4,5];
my $row3 = [6,7,8];

push @array_2d, $row1;
push @array_2d, $row2;
push @array_2d, $row3;


which could be rewritten simpler as follows:


Code
my $row1 = [0,1,2]; 
my $row2 = [3,4,5];
my $row3 = [6,7,8];

push @array_2d, $row1, $row2, $row3;


Or, yet even more concise:


Code
my @array_2d = ([0,1,2], [3,4,5], [6,7,8]);



zapzap
User

Oct 6, 2013, 1:14 PM

Post #10 of 10 (2045 views)
Re: [perldeveloer] 2d array in sub [In reply to] Can't Post

Can you post what you have so far?

 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives