Unexpected subroutine behaviour without &

K

Kevin Collins

Hi,

I've just stumbled across a strange problem on my HP-UX systems. The
following code will demonstrate the problem:

#!/usr/bin/perl -w

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray
{
# This function takes an array reference and returns
# an array without dups

# local variables
my %tmp;
my @returns;

foreach (@list)
{
# if the hash element has a value, then don't
# add it to return list since we've already added it
push (@returns, $_) unless ($tmp{$_});
$tmp{$_}++;
}

return @returns;
}

I have two different "home built" versions of Perl (5.6.1 and 5.8.0) and two
HP-supplied versions (5.6.1 and 5.8.0).

The output I would expect:

aa bb cc dd ee zz
aa bb cc dd ee zz

However, here is what I see:

Custom 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

Custom 5.8.0:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

HP's 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

HP's 5.8.0:

aa bb cc dd ee zz
aa bb cc dd ee zz

I also checked Perl 5.8.0 on RedHat 9.0 and see my expected results. I'm
wondering why I need to call &UniqArray(@list) instead of UniqArray(@list)? I
started looking into this when a new script was not giving me a correctly
sorted list. Is there some know issue with this?

Thanks,

Kevin
 
G

Gunnar Hjalmarsson

Kevin said:
I've just stumbled across a strange problem on my HP-UX systems.
The following code will demonstrate the problem:

#!/usr/bin/perl -w

Why the -w switch? "use warnings;" is enough.
my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";

Considering the code in UniqArray(), you could as well just say:

print join(" ", sort UniqArray()) . "\n";
sub UniqArray
{
# This function takes an array reference

No, it doesn't. It takes the array @list, which is a package lexical.
The list in @_, which contains copies of the elements in @list since
you passed the array (not array reference) @list, is not used.
I'm wondering why I need to call &UniqArray(@list) instead of
UniqArray(@list)?

Can't explain that.
 
D

David K. Wall

Gunnar Hjalmarsson said:
Kevin Collins wrote:

No, it doesn't. It takes the array @list, which is a package
lexical. The list in @_, which contains copies of the elements in
@list since you passed the array (not array reference) @list, is
not used.

It could be a bit shorter, too.

sub UniqArray {
# This function takes an array reference and returns
# an array without dups

my %seen;
return grep {not $seen{$_}++} @{$_[0]};
}
Can't explain that.

Me neither.
 
K

Kevin Collins

Why the -w switch? "use warnings;" is enough.

True - I added the "use warnings" to the script because I was running:

/some/path/to/perl myscript
/some/other/path/to/perl myscript

and wanted to make sure I had warnings enabled (with using -w on the command
line).
Considering the code in UniqArray(), you could as well just say:

print join(" ", sort UniqArray()) . "\n";

Damn - I hate it when I blunder a code post! I incorrectly posted the wrong
version of my test code. That foreach line should be:

foreach (@_)
 
B

Ben Morrow

Quoth Kevin Collins said:
Hi,

I've just stumbled across a strange problem on my HP-UX systems. The
following code will demonstrate the problem:

#!/usr/bin/perl -w

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray
{
# This function takes an array reference and returns
# an array without dups

# local variables
my %tmp;
my @returns;

foreach (@list)

Well, there's a bug here. You are accessing the global @list directly;
what you mean is

for (@_) {

This makes no difference though...
{
# if the hash element has a value, then don't
# add it to return list since we've already added it
push (@returns, $_) unless ($tmp{$_});
$tmp{$_}++;
}

return @returns;
}

I have two different "home built" versions of Perl (5.6.1 and 5.8.0) and two
HP-supplied versions (5.6.1 and 5.8.0).

The output I would expect:

aa bb cc dd ee zz
aa bb cc dd ee zz

However, here is what I see:

Custom 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

Custom 5.8.0:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

HP's 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

HP's 5.8.0:

aa bb cc dd ee zz
aa bb cc dd ee zz

I also checked Perl 5.8.0 on RedHat 9.0 and see my expected results.

Using

This is perl, v5.8.2 built for i686-linux-thread-multi

I get the following results:

#!/usr/bin/perl -l

my @list = qw/cc aa bb aa dd zz ee/;

sub uniq {
my (@r, %t);

# print "ARGS:", @_;

for (@_) {
push @r, $_ unless $t{$_};
$t{$_}++;
}

return @r;
}

print sort uniq @list;
print sort uniq(@list);
print sort(uniq @list);
print sort(uniq(@list));
print sort &uniq(@list);

__END__

ee zz dd aa bb aa cc
ee zz dd aa bb aa cc
ee zz dd aa bb aa cc
aa bb cc dd ee zz
aa bb cc dd ee zz

Uncommenting the 'print "ARGS:"' line makes it clear what is happening:
in the first three cases, perl is using uniq as the sort subroutine.
This is probably a bug of some sort; I would say that at least my second
test ought to produce the expected results.

Ben
 
K

Kevin Collins

Hi,

I've just stumbled across a strange problem on my HP-UX systems. The
following code will demonstrate the problem:

#!/usr/bin/perl -w

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray
{
# This function takes an array reference and returns
# an array without dups

# local variables
my %tmp;
my @returns;

foreach (@list)
{
# if the hash element has a value, then don't
# add it to return list since we've already added it
push (@returns, $_) unless ($tmp{$_});
$tmp{$_}++;
}

return @returns;
}

The code above was not quite correct (I inadvertantly included a different
version of the code). It should be:

#!/usr/bin/perl

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray
{
# This function takes an array and returns
# an array without dups

# local variables
my %tmp;
my @returns;

foreach (@_)
{
# if the hash element has a value, then don't
# add it to return list since we've already added it
push (@returns, $_) unless ($tmp{$_});
$tmp{$_}++;
}

return @returns;
}
 
A

Anno Siegel

Kevin Collins said:
Hi,

I've just stumbled across a strange problem on my HP-UX systems. The
following code will demonstrate the problem:

#!/usr/bin/perl -w

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray

[definition snipped]
I have two different "home built" versions of Perl (5.6.1 and 5.8.0) and two
HP-supplied versions (5.6.1 and 5.8.0).

The output I would expect:

aa bb cc dd ee zz
aa bb cc dd ee zz

However, here is what I see:

Custom 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

[more similar results]

Perl's "sort" is a peculiar beast in that its (optional) first argument
can be the name of a subroutine. The name is given as a bareword, and
so

sort(UniqArray(@list))

is ambiguous syntax. It can be parsed as intended, or as a sort where
UniqArray is used as the comparison routine. The parentheses around
@list in the second reading are redundant, but possible.

"&UniqArray" apparently forces the intended interpretation, but both
are possible. Why different builds of Perl differ in their
interpretation is another question.

I snipped the code in UniqArray because it has nothing to do with the
issue, but actually it would deserve comment also.

Anno
 
K

Kevin Collins

^^^^^

That might as well not be there, UniqArray() never examines its arguments.





You should not communicate with subroutines via global variables,
you should pass arguments instead.

Yes, I know - see my follow-up post where I included the "correct" code...

Kevin
 
A

Anno Siegel

Lexical variables are independent of any package, the term "package
lexical" makes no sense. @list is a a file-global lexical array.

[correct explanation snipped]

Anno
 
K

Kevin Collins

Well, there's a bug here. You are accessing the global @list directly;
what you mean is

for (@_) {

Yeah, I've reposted with the correct code.
This makes no difference though...


Using

This is perl, v5.8.2 built for i686-linux-thread-multi

I get the following results:

#!/usr/bin/perl -l

my @list = qw/cc aa bb aa dd zz ee/;

sub uniq {
my (@r, %t);

# print "ARGS:", @_;

for (@_) {
push @r, $_ unless $t{$_};
$t{$_}++;
}

return @r;
}

print sort uniq @list;
print sort uniq(@list);
print sort(uniq @list);
print sort(uniq(@list));
print sort &uniq(@list);

__END__

ee zz dd aa bb aa cc
ee zz dd aa bb aa cc
ee zz dd aa bb aa cc
aa bb cc dd ee zz
aa bb cc dd ee zz

I've just run your code and this is the output I see:

ccaabbaaddzzee
ccaabbaaddzzee
ccaabbaaddzzee
ccaabbaaddzzee
aabbccddeezz

I'm not clear at all how you are getting the results you've shown...
 
K

Kevin Collins

Kevin Collins said:
Hi,

I've just stumbled across a strange problem on my HP-UX systems. The
following code will demonstrate the problem:

#!/usr/bin/perl -w

use strict;
use warnings;

my @list = qw(cc aa bb aa dd zz ee);
print join(" ", sort(UniqArray(@list))) . "\n";
print join(" ", sort(&UniqArray(@list))) . "\n";

sub UniqArray

[definition snipped]
I have two different "home built" versions of Perl (5.6.1 and 5.8.0) and two
HP-supplied versions (5.6.1 and 5.8.0).

The output I would expect:

aa bb cc dd ee zz
aa bb cc dd ee zz

However, here is what I see:

Custom 5.6.1:

ee zz dd aa bb aa cc
aa bb cc dd ee zz

[more similar results]

Perl's "sort" is a peculiar beast in that its (optional) first argument
can be the name of a subroutine. The name is given as a bareword, and
so

sort(UniqArray(@list))

is ambiguous syntax. It can be parsed as intended, or as a sort where
UniqArray is used as the comparison routine. The parentheses around
@list in the second reading are redundant, but possible.

"&UniqArray" apparently forces the intended interpretation, but both
are possible. Why different builds of Perl differ in their
interpretation is another question.

That makes sense to me. Honestly, now that I read what you've written and
look closer at 'perldoc -f sort', I am surprised this hasn't burned me before.

Its possible that in other cases I am using a Perl builtin function and Perl
just handles it correctly.
I snipped the code in UniqArray because it has nothing to do with the
issue, but actually it would deserve comment also.

Yes - shot myself in the foot by posting the wrong code. I did repost the
correct code.


Thanks,

Kevin
 
B

Ben Morrow

Quoth Kevin Collins said:
I've just run your code and this is the output I see:

ccaabbaaddzzee
ccaabbaaddzzee
ccaabbaaddzzee
ccaabbaaddzzee
aabbccddeezz

I'm not clear at all how you are getting the results you've shown...

Well, except for the fact that I missed out the

$, = ' ';

line, presumably this is yet another difference between perl versions...
actually, I'm not quite sure why the list is reversed. 5.8's sort is supposed
to be stable...

Ben
 
P

Paul Lalli

actually, I'm not quite sure why the list is reversed. 5.8's sort is supposed
to be stable...

The sort subroutine is returning the array @r. Because %t is a lexical,
it will be empty each time the sort subroutine is called. Therefore, the
size of @r each time the routine is called will be either 1 or 2 (it will
be 1 only in the case where two identical elements are being compared).
Both of these values are positive, which is the means of telling sort to
put $b before $a. Assuming sort only compares two elements in the
original order (that is, $a is always listed before $b in the original
list), it is easy to see why the results would come back in the reverse
order.

Paul Lalli
 
G

Gunnar Hjalmarsson

Anno said:
Lexical variables are independent of any package, the term "package
lexical" makes no sense. @list is a a file-global lexical array.

Thanks for the correction. But I had to test:

use strict;
use warnings;

package A;
my $lexical = 'apple';
our $global = 'orange';

package B;
#my $lexical = 'banana';
print '$lexical = ', $lexical, "\n";
print '$global = ', ($global or '(empty)'), "\n";

I couldn't do "my $lexical" once again without a warning, but I
noticed something else that surprised me. The above code outputs:
$lexical = apple
$global = orange

while I had expected:
$lexical = apple
$global = (empty)

When Perl doesn't find a global variable in the current package, does
it look for a varable with the same name in other packages?
 
B

Ben Morrow

Quoth Gunnar Hjalmarsson said:
use strict;
use warnings;

package A;
my $lexical = 'apple';
our $global = 'orange';

package B;
#my $lexical = 'banana';
print '$lexical = ', $lexical, "\n";
print '$global = ', ($global or '(empty)'), "\n";

I couldn't do "my $lexical" once again without a warning, but I
noticed something else that surprised me. The above code outputs:
$lexical = apple
$global = orange

while I had expected:
$lexical = apple
$global = (empty)

When Perl doesn't find a global variable in the current package, does
it look for a varable with the same name in other packages?

Nope. our declares a lexical name in the current scope which is bound to
the global variable of the same name in the current package. If you
change your 'our' to 'use vars', it'll do what you expect (or rather,
give a strict error).

Ben
 
G

Gunnar Hjalmarsson

Ben said:
Nope. our declares a lexical name in the current scope which is
bound to the global variable of the same name in the current
package. If you change your 'our' to 'use vars', it'll do what you
expect (or rather, give a strict error).

Hmm.. So that's the difference between "use vars" and "our"... Since
I still care about 5.005 compatibility, I haven't really begun to use
"our". Sometimes I wonder if I ever will master even the basics of
Perl. :(

Anyway, thanks Ben and Anno for the lesson!
 

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,768
Messages
2,569,574
Members
45,048
Latest member
verona

Latest Threads

Top