Need help passing arrays by reference pls.

G

G Klinedinst

Hi all. I have a question I have been pulling my hair out all day
over. Can someone tell me what is happening in the following code. The
first print statements create the results I expect. The subroutines
statments print arrays with 1 element but not data in [0]. What gives?
I am passing by reference and then dereferencing in the sub. Can you
point me towards what I am missing? I have looked at the perlfaqs and
read the perldocs on references but it I can't find what I am looking
for. It could also be my sleep deprived mind is seeing it but not
grokking it. TIA.

-Greg

***********CODE****************
#!/usr/local/bin/perl

use strict;
use warnings;

my @arr1;
my @arr2;

$arr1[0] = 1;

print( $arr1[0] . ":" . scalar( @arr1 ) . "\n" );
print( $arr2[0] . ":" . scalar( @arr2 ) . "\n" );
print( "\n\n" );

test( \@arr1, \@arr2 );

sub test
{
my @subArr1 = @$_[0];
my @subArr2 = @$_[1];

print( $subArr1[0] . ":" . scalar( @subArr1 ) . "\n" );
print( $subArr2[0] . ":" . scalar( @subArr2 ) . "\n" );
}
**************/CODE***********

**************OUTPUT*********
1:1
Use of uninitialized value in concatenation (.) or string at
../arrays.pl
line 12.
:0


Use of uninitialized value in concatenation (.) or string at
../arrays.pl
line 22.
:1
Use of uninitialized value in concatenation (.) or string at
../arrays.pl
line 23.
:1
*************/OUTPUT**********
 
W

Walter Roberson

:Hi all. I have a question I have been pulling my hair out all day
:eek:ver. Can someone tell me what is happening in the following code.

:test( \@arr1, \@arr2 );

:sub test
:{
: my @subArr1 = @$_[0];
: my @subArr2 = @$_[1];

Looks like you have a precidence problem. @$_[0] is {@{$_}}[0]
Your code will work if you use

my @subArr1 = @{$_[0]};
my @subArr2 = @{$_[1]};


I would, though, recommend using prototypes and declaring sub test
before it is used:

sub test( \@\@ ) {
my @subArr1 = @{$_[0]};
my @subArr2 = @{$_[1]};
# ...
}

test @arr1, @arr2;


Notice there that you do NOT explicitly \ the arrays as you pass them in.


Personally, I wouldn't take a copy of the array in the sub unless
I had a reason to. I would use something akin to

sub test( \@\@ ) {
my ($subArr1_ref, $subArr2_ref) = @_;
print $subArr1_ref->[0] . ':' . scalar( @$subArr1_ref ) . "\n";
print $subArr2_ref->[0] . ':' . scalar( @$subArr2_ref ) . "\n";
}
 
A

A. Sinan Unur

(e-mail address removed) (G Klinedinst) wrote in
Hi all. I have a question I have been pulling my hair out all day
over. Can someone tell me what is happening in the following code. The
first print statements create the results I expect. The subroutines
statments print arrays with 1 element but not data in [0]. What gives?
I am passing by reference and then dereferencing in the sub. Can you
point me towards what I am missing? I have looked at the perlfaqs and
read the perldocs on references but it I can't find what I am looking
for. It could also be my sleep deprived mind is seeing it but not
grokking it. TIA.

-Greg

***********CODE****************
#!/usr/local/bin/perl

use strict;
use warnings;

my @arr1;
my @arr2;

$arr1[0] = 1;

print( $arr1[0] . ":" . scalar( @arr1 ) . "\n" );
print( $arr2[0] . ":" . scalar( @arr2 ) . "\n" );
print( "\n\n" );

test( \@arr1, \@arr2 );

sub test
{
my @subArr1 = @$_[0];
my @subArr2 = @$_[1];

Life would be much easier if you did:

#! perl

use strict;
use warnings;

my @arr1 = (1, 2, 3);
my @arr2 = (4, 5, 6);

$arr1[0] = 1;

print $arr1[0], ':', scalar @arr1, "\n" ;
print $arr2[0], ':', scalar @arr2, "\n" ;

# You should avoid the unnecessary concatenations you had in your code.

print "\n\n" ;

test(\@arr1, \@arr2);

sub test {
my ($subArr1, $subArr2) = @_;

print $subArr1->[0], ':', scalar @{$subArr1}, "\n" ;
print $subArr2->[0], ':', scalar @{$subArr2}, "\n" ;
}
 
G

G Klinedinst

Looks like you have a precidence problem. @$_[0] is {@{$_}}[0]
Your code will work if you use

my @subArr1 = @{$_[0]};
my @subArr2 = @{$_[1]};

Thanks, that worked perfectly. I see what I was doing now.
I would, though, recommend using prototypes and declaring sub test
before it is used:

sub test( \@\@ ) {
my @subArr1 = @{$_[0]};
my @subArr2 = @{$_[1]};
# ...
}

test @arr1, @arr2;

Notice there that you do NOT explicitly \ the arrays as you pass them in.

Yep, I am just not sure why. I will need to study the docs this
weekend and figure out what you are doing here.

Personally, I wouldn't take a copy of the array in the sub unless
I had a reason to. I would use something akin to

sub test( \@\@ ) {
my ($subArr1_ref, $subArr2_ref) = @_;
print $subArr1_ref->[0] . ':' . scalar( @$subArr1_ref ) . "\n";
print $subArr2_ref->[0] . ':' . scalar( @$subArr2_ref ) . "\n";
}

Makes sense. The reason I am making a local copy is that I am going to
be iterating through them(using a for loop). With your syntax I'm not
sure how I would go about that. For example I will be doing something
like this:

for my $a ( @subArr1 ) {
print $a;
}

If I can figure out how to do that using the references, like you are
using I will do that otherwise I will have to make a local copy.
Thanks again.


-Greg
 
W

Walter Roberson

:Makes sense. The reason I am making a local copy is that I am going to
:be iterating through them(using a for loop). With your syntax I'm not
:sure how I would go about that. For example I will be doing something
:like this:

:for my $a ( @subArr1 ) {
: print $a;
:}

:If I can figure out how to do that using the references, like you are
:using I will do that otherwise I will have to make a local copy.

print $_ foreach @$subArr1_ref

Similarily,

print "$_: $hash_ref->{$_}\n" foreach (keys %$hash_ref);

print $$scalar_ref, "\n"
 
G

G Klinedinst

Life would be much easier if you did:
#! perl

use strict;
use warnings;

my @arr1 = (1, 2, 3);
my @arr2 = (4, 5, 6);

$arr1[0] = 1;

print $arr1[0], ':', scalar @arr1, "\n" ;
print $arr2[0], ':', scalar @arr2, "\n" ;

Unfortunately the arrays hold an index to another array, which
basically tells which input fields were filled out, so some of the
arrays are going to be empty(due to the user not entering any data at
that position).

# You should avoid the unnecessary concatenations you had in your code.

print "\n\n" ;

test(\@arr1, \@arr2);

sub test {
my ($subArr1, $subArr2) = @_;

print $subArr1->[0], ':', scalar @{$subArr1}, "\n" ;
print $subArr2->[0], ':', scalar @{$subArr2}, "\n" ;
}

Why is that? Are the commas implemented faster, or just for
readability? Just curious. It's a habit I picked up from Java so now
even my Perl code looks like that. It's hard to break coding habits
once you get into them. Thanks for your help, and I hope things are
starting to warm up there in Ithaca for you.

-Greg
 
W

Walter Roberson

:> # You should avoid the unnecessary concatenations you had in your code.

:Why is that? Are the commas implemented faster, or just for
:readability?

Commas in print's are converted into concatenation internally, so both
have the same execution speed.

Sometimes, though, if you use concatenation, you can end up
with unexpected results because of precidence issues. Just a couple
of days ago, I had something of the form

print Operation Argument . Somestring

and Operation was being applied to Argument . Somestring
instead of Operation Argument being computed and Somestring being
concatenated on the end. My fault for not thinking about precidences.
Changing to , instead of . fixed the code.
 
T

Tad McClellan

G Klinedinst said:
for my $a ( @subArr1 ) {
If I can figure out how to do that using the references, like you are
using I will do that


Apply "Use Rule 1" from perlreftut.pod:

for my $a ( @subArr1 ) { # pretend it is a regular array

for my $a ( @{ } ) { # replace the array _name_ with a block...

for my $a ( @{ $subArr1_ref } ) { # ... that returns a ref to an array

(and in this case you can drop the block's curlies)
 
K

ko

Walter said:
:> # You should avoid the unnecessary concatenations you had in your code.

:Why is that? Are the commas implemented faster, or just for
:readability?

Commas in print's are converted into concatenation internally, so both
have the same execution speed.

Got a question about the paragraph above ...

[snip]

Remember reading somewhere that there is a difference between the two, so I
tried this:

bash-2.05b$ cat bench && bench
#!/usr/bin/perl -w
use strict;
use Benchmark;

my $str = 'x' x 100_000;
my $null = ($^O !~ /win/i) ? '/dev/null' : 'nul';
open(NULL, ">$null") or die $!;

timethese(10_000, {
    commas => sub {print NULL $str, $str, $str, $str},
    concat => sub {print NULL $str . $str . $str . $str}
});

__END__
Benchmark: timing 10000 iterations of commas, concat...
commas:  3 wallclock secs ( 0.67 usr +  1.71 sys =  2.38 CPU)
@ 4196.72/s (n=10000)

concat:  6 wallclock secs ( 3.88 usr +  1.70 sys =  5.58 CPU)
@ 1792.72/s (n=10000)

Please correct the usage if I made a mistake - rarely use Benchmark, as I
still have a lot to learn and am trying to concentrate on writing readable
good code versus writing fast code. But I would like to understand why
there is a speed difference - is it actually the case that 'Commas in
print's are converted into concatenation internally'. Is this even
something to be concerned about?

thanks in advance - keith
 
W

Walter Roberson

:> Commas in print's are converted into concatenation internally, so both
:> have the same execution speed.

:Got a question about the paragraph above ...

You think I can find the part of the documentation I was thinking of?
No.... :(
 
A

A. Sinan Unur

(e-mail address removed) (G Klinedinst) wrote in
Unfortunately the arrays hold an index to another array, which
basically tells which input fields were filled out, so some of the
arrays are going to be empty(due to the user not entering any data at
that position).

In that case, maybe parallel arrays are not the right data structure for
the problem. How about using a hash?
# You should avoid the unnecessary concatenations you had in
# your code.

print "\n\n" ;

test(\@arr1, \@arr2);

sub test {
my ($subArr1, $subArr2) = @_;

print $subArr1->[0], ':', scalar @{$subArr1}, "\n" ;
print $subArr2->[0], ':', scalar @{$subArr2}, "\n" ;
}

Why is that? Are the commas implemented faster, or just for
readability?

Well, at least in my mind, the above is an improvement in readability and
maintainability.
Just curious. It's a habit I picked up from Java so now
even my Perl code looks like that.

I know and hate the constant String additions in Java :)
I hope things are starting to warm up there in Ithaca for you.

Just a little.

Sinan.
 
B

Ben Morrow

ko said:
Got a question about the paragraph above ...

Remember reading somewhere that there is a difference between the two, so I
tried this:
<snip benchmark>

A tool I like for answering this sort of question is B::Graph, which
produces a graph of the optree. It seems that

print "a", "b";

becomes

nextstate -> pushmark -> const -> const -> print

(nextstate starts a new statement. pushmark makes a mark in the Perl
stack to show where the arguments for the print begin. The consts push
items onto the stack, the print takes them off down to the mark and
prints them) whereas

print "a" . "b";

becomes

nextstate -> pushmark -> const -> print

ie. perl will perform the concatenation at compile time, and thus it
must be more efficient. However,

print "a", $x;

becomes

nextstate -> pushmark -> const -> padsv -> print

as before (padsv gets the value of the variable and pushes it onto the
stack) whereas

print "a" . $x;

becomes

nextstate -> pushmark -> const -> padsv -> concat -> print

ie. the concatenation is done as a separate step before the print, so
it may well be slower (this depends on whether printing several items
as opposed to one is slower or faster than concatenating them).

Certainly it is not true that they are the same: I suspect Walter was
thinking of

print "a$x";

which *is* converted into

print "a" . $x;

internally.

Ben
 
K

ko

Ben Morrow wrote:

[snip]
A tool I like for answering this sort of question is B::Graph, which
produces a graph of the optree. It seems that

print "a", "b";

becomes

nextstate -> pushmark -> const -> const -> print

(nextstate starts a new statement. pushmark makes a mark in the Perl
stack to show where the arguments for the print begin. The consts push
items onto the stack, the print takes them off down to the mark and
prints them) whereas

print "a" . "b";

becomes

nextstate -> pushmark -> const -> print

ie. perl will perform the concatenation at compile time, and thus it
must be more efficient. However,

print "a", $x;

becomes

nextstate -> pushmark -> const -> padsv -> print

as before (padsv gets the value of the variable and pushes it onto the
stack) whereas

print "a" . $x;

becomes

nextstate -> pushmark -> const -> padsv -> concat -> print

ie. the concatenation is done as a separate step before the print, so
it may well be slower (this depends on whether printing several items
as opposed to one is slower or faster than concatenating them).

Certainly it is not true that they are the same

[snip]

Perl's internals are over my head, but thank you for providing a simple,
concise explanation that makes sense. Have never used any of the B
modules and tried to play around with B::Graph, but couldn't figure out
how to get output like yours :( . But there's a link at the bottom of
the documentation, so I'll look at that and at perlguts.

keith
 
B

Ben Morrow

ko said:
Have never used any of the B
modules and tried to play around with B::Graph, but couldn't figure out
how to get output like yours :( . But there's a link at the bottom of
the documentation, so I'll look at that and at perlguts.

You need dot from http://www.research.att.com/sw/tools/graphviz/; then
run

perl -MO=Graph,-dot -e'...program...' | dot -Tps > ops.ps

.. Then look at ops.ps with a PostScript viewer. If you prefer, you can
use -Tgif for GIF output.

I have found that anything more complex than is reasonable on the
command-line will crash dot :(.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top