@_ in subroutine corrupts data?

S

synthespian

hello --

I can find no reasonable explanation for this behavior. Not theat
there's isn't one (a trivial one at that), but I am having a hard time
finding the reason for this:

#!/usr/bin/perl

@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;
foreach (@array) {
print "$array[$_]\n";
}
return (@array);
}

print "Finished\n";

$ perl subrout2.pl
2
3
4

Finished


Whatever happened to the "1"?
Is this behavior to be expected? Why?
I get the same response in Perl 5.6 and 5.8

TIA

Henry
 
S

Sam Holden

hello --

I can find no reasonable explanation for this behavior. Not theat
there's isn't one (a trivial one at that), but I am having a hard time
finding the reason for this:

#!/usr/bin/perl

you are missing use strict; and use warnings; (or -w).
@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;
foreach (@array) {

That will set $_ to 1, then 2, then 3, then 4.
print "$array[$_]\n";

Since arrays start at element 0 (well, in sane code anyway) that will
print the second element when $_ is 1, the third when $_ is 2, the
fourth when $_ is 3, and the fifth when $_ is 4.
}
return (@array);
}

print "Finished\n";

$ perl subrout2.pl
2
3
4

Notice the blank line, that's $array[4] which is undef.
Finished


Whatever happened to the "1"?

You didn't print it.
Is this behavior to be expected? Why?

Yes. Values aren't the same an indexes.
 
C

Chris Dutton

synthespian said:
#!/usr/bin/perl

@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;
foreach (@array) {

On each iteration $_ contains an actual element of the array, not just the
index.
print "$array[$_]\n";

This should be written: print "$_\n";

As it is, you're using the item to do a look-up on the array. You've
already got this via foreach. The fact that you're getting anything close
to a correct output is...
}
return (@array);
}

print "Finished\n";

$ perl subrout2.pl
2
3
4

.... that the items in the array are (for the most part) valid indices for
that array, except that arrays in Perl begin with index zero, and your array
doesn't contain zero.

Also, by using @array in the "main" scope of your program, and inside the
"subthis" subroutine, your "@array = @_" line is always going to overwrite
the @array in the main scope.

The following does not have this problem.

#!/usr/bin/perl

@array = subthis(1, 2, 3, 4);

sub subthis {
my @array = @_;
foreach my $item (@array) {
print "$item\n";
}
return @array;
}
 
S

synthespian

Sam said:
hello --

I can find no reasonable explanation for this behavior. Not theat
there's isn't one (a trivial one at that), but I am having a hard time
finding the reason for this:

#!/usr/bin/perl


you are missing use strict; and use warnings; (or -w).

@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;
foreach (@array) {


That will set $_ to 1, then 2, then 3, then 4.

print "$array[$_]\n";


Since arrays start at element 0 (well, in sane code anyway) that will
print the second element when $_ is 1, the third when $_ is 2, the
fourth when $_ is 3, and the fifth when $_ is 4.

Yes, it's obvious! Thanks to you and Chris Dutton for taking the time.
I thought that what would be set to $_ was the /number/ of elements in
@array, and not 1, 2, 3, 4, and that this number would start at 0.
Like Dave said, "Since arrays are 0-indexed, you are getting three
values and an undefined
fourth value. I think what you meant to say was:
print "$_\n";
Instead of:
print "$array[$_]\n";

So this is about context, is it? $_ will be the placeholder for an
array indexed when $array[$_], otherwise it is not 0-indexed, that is,
is $_ in the case of print "$_\n" evaluating to the number of elements?
Or is it a placeholder for the /elements/ themselves?
What's up with this (perceived) inconsistency?

Thanks for answering.

Regs,

Henry
 
S

synthespian

While we're on this, it got weird with "strict" and "warnings". Why?


@arrayb = subthat(1, 2, 3, 4);

sub subthat {
@arrayb = @_;
foreach (@arrayb) {
print "$_\n";
}
}

print "Finished\n";

$ perl subrout3.pl
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 5.
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 9.
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 10.
Execution of subrout3.pl aborted due to compilation errors.

TIA
Henry
 
S

Sam Holden

While we're on this, it got weird with "strict" and "warnings". Why?

perldoc strict
@arrayb = subthat(1, 2, 3, 4);

sub subthat {
@arrayb = @_;
foreach (@arrayb) {
print "$_\n";
}
}

print "Finished\n";

$ perl subrout3.pl
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 5.
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 9.
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 10.
Execution of subrout3.pl aborted due to compilation errors.


The section marked "strict vars" is the relevant bit.
 
I

Iain Truskett

* synthespian <[email protected]>:


[...]
So this is about context, is it? $_ will be the placeholder for an
array indexed when $array[$_], otherwise it is not 0-indexed, that is,
is $_ in the case of print "$_\n" evaluating to the number of
elements? Or is it a placeholder for the /elements/ themselves?
What's up with this (perceived) inconsistency?

There's no inconsistency. In a loop such as:

foreach (@array) {
...
}

$_ is set to each element of the array in turn. At no point does $_
contain an array index or the array size except by mathematical
coincidence (that is, the numbers happen to be the same).

The following code should illustrate the point (yes, it's mostly your
code, I just tweaked what it prints and declared some variables and give
it different input):

#!/usr/bin/perl
my @words = subthis( "hello", "xyzzy", "plugh" );

sub subthis {
my @array = @_;
foreach (@array) {
print "$_\n";
}
return @array;
}



cheers,
 
C

Chris Dutton

synthespian said:
While we're on this, it got weird with "strict" and "warnings". Why?


@arrayb = subthat(1, 2, 3, 4);

sub subthat {
@arrayb = @_;
foreach (@arrayb) {
print "$_\n";
}
}

print "Finished\n";

$ perl subrout3.pl
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 5.

As I mentioned in my earlier reply, when you assign to @arrayb in your
subroutine, you're not automatically creating a new array. Rather you're
assigning to the @arrayb that's already in the "main" package. Now, while
you can do this, it's not advisable since you can get unexpected results.

You can use the "my" keyword to create a new array inside the subroutine
which has the same name as one in the outer scope.

@arrayb = subthat(1, 2, 3, 4);

sub subthat {
my @arrayb = @_;
foreach (@arrayb) {
print "$_\n";
}
# and I presume from your previous subroutine,
# and how this one is used, that you want @arrayb
# returned.
return @arrayb;
}
 
C

Chris Dutton

synthespian said:
Yes, it's obvious! Thanks to you and Chris Dutton for taking the time.
I thought that what would be set to $_ was the /number/ of elements in
@array, and not 1, 2, 3, 4, and that this number would start at 0.

To get the number of elements in an array, assign it to a scalar, like so:

my @someArray = (1,2,3,4);
my $lengthOfArray = @someArray;
print "$lengthOfArray\n";
# prints:
# 4
So this is about context, is it? $_ will be the placeholder for an
array indexed when $array[$_], otherwise it is not 0-indexed, that is,
is $_ in the case of print "$_\n" evaluating to the number of elements?
Or is it a placeholder for the /elements/ themselves?

$_ is just a helpful timesaver. It can be a shortcut in that many of Perl's
built-ins use it if no argument is provided. For instance, "print;" is
equivalent to "print $_;".

In the foreach loop, $_ serves as a shortcut to each element of the array.
This can be easily replaced with a more meaningful name if your goal is
long-term maintainability.

foreach (@foo) { print }

is equivalent to:

foreach my $item (@foo) { print $item }
 
T

Tad McClellan

See the "Foreach Loops" section in perlsyn.pod.

@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;
foreach (@array) {


That will set $_ to 1, then 2, then 3, then 4.

print "$array[$_]\n";


Since arrays start at element 0 (well, in sane code anyway) that will
print the second element when $_ is 1, the third when $_ is 2, the
fourth when $_ is 3, and the fifth when $_ is 4.

Yes, it's obvious!

So this is about context, is it?


If you mean list context vs. scalar context, then no, this
is not about context.

This is about how foreach loops work in Perl.

$_ will be the placeholder for an
array indexed when $array[$_], otherwise it is not 0-indexed, that is,


You can manipulate lists using foreach without any explicit
indexing at all.

is $_ in the case of print "$_\n" evaluating to the number of elements?
Or is it a placeholder for the /elements/ themselves?


The correct term is "alias" rather than "placeholder".

What's up with this (perceived) inconsistency?


It sounds like you haven't read up on the operation of foreach.
 
T

Tad McClellan

synthespian said:
While we're on this, it got weird with "strict" and "warnings". Why?


use warnings has nothing to do with it, there are no warning
messages below.

@arrayb = subthat(1, 2, 3, 4);
$ perl subrout3.pl
Global symbol "@arrayb" requires explicit package name at subrout3.pl
line 5.


When you put "use strict" in your program, you are making this
promise to perl:

I will declare all of my variables before I use them.

If you break your promise, perl will refuse to run your program.

You are using @arrayb without declaring it, you've broken your promise.

So, either declare the variable:

my @arrayb = subthat(1, 2, 3, 4);

or use an explicit package name:

@main::arrayb = subthat(1, 2, 3, 4);
 
B

bd

synthespian said:
hello --

I can find no reasonable explanation for this behavior. Not theat
there's isn't one (a trivial one at that), but I am having a hard time
finding the reason for this:

#!/usr/bin/perl

@array = subthis(1, 2, 3, 4);

sub subthis {

@array = @_;

foreach (@array) {

This is the same as foreach(1,2,3,4) {
print "$array[$_]\n";

So this line prints $array[1], $array[2], etc. Since array numbering starts
at 0, it prints the last 3 elements, plus a nonexistent one. Perhaps you
meant:

foreach(@array){
print "$_\n";
}

Or:
for(my $i = 0; $i <= $#array; $i++){
print "$array[$_]\n";
}

[snip]
 
B

bd

synthespian said:
While we're on this, it got weird with "strict" and "warnings". Why?


@arrayb = subthat(1, 2, 3, 4);

Strict requires most variables to be explicitly declared (perlvar has the
exceptions). Try:
my @arrayb = subthat(1, 2, 3, 4);
sub subthat {
@arrayb = @_;

And my @arrayb = @_; here, so as not to corrupt the other @arrayb
 

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,766
Messages
2,569,569
Members
45,044
Latest member
RonaldNen

Latest Threads

Top