3

I encountered a strange bug while using @_ to pass a single argument to a Perl subroutine. The value passed to a subroutine changes right after entering the subroutine.

Code example:

my $my_def = 0;
print "my_def = $my_def \n";
@someResult = doSomething($my_def);

sub doSomething {
    my $def = @_;
    print "def = $def \n";
    ...
}

This returned:

> my_def = 0
> def = 1  # instead of "0"

One more strange thing is that the code worked right for several months before.

The problem was resolved, when I changed it to:

sub doSomething {
    my $def = $_[0];

Could anyone tell what could cause the problem? Are there any limitations in using @_ to pass a single argument?

Thanks!

3 Answers 3

13

You're getting the correct behaviour, although it isn't what you expected.

A simple rule of thumb for getting local variables from the arguments in a subroutine is to always use parentheses around the variable list in the my (...) declaration:

sub do_something
{
    my ($def) = @_;
    ...
}

The difference is between list context and scalar context. In scalar context, all arrays return the number of elements in the array: 1 in your case. And when you wrote my $def = @_ you provided scalar context. When you used my $def = $_[0] instead you explicitly accessed element zero of the array, which is a scalar (hence the $ sigil) so it all worked again.

In the general case you might have:

sub do_something_else
{
    my ($arg1, $arg2, $arg3, @the_rest) = @_;
    ...
}

Now you have three scalar local variables, $arg1, $arg2, and $arg3, and one array, @the_rest that collects any extra arguments passed.

Sign up to request clarification or add additional context in comments.

Comments

1

The answer is simple when a array assigned to scalar value it returns number of elements in the array

2 Comments

Welcome to Stack Overflow. Your answer contains the core information that is correct, so I've given you an upvote, but I think you would do better if you explained a bit more about what you mean. The person asking the question probably can't tell from your answer what they need to do to fix their problem (or, since they've worked out that my $def = $_[0]; works, why the version using @_ doesn't work). This is the difference between a minimal answer and a good answer. (If half-votes were an option, you'd probably only have gotten a half-vote from me.)
Thanks for the inputs will definitely improve my way of answering the que.
1

It's all about context. Here is an example:

@data = (0, 1, 2);

$count = @data;        # imply in scalar context
### $count: 3

$count = scalar @data; # same as above, but force scalar context
### $count: 3

$first = $data[0];     # both sides are in scalar context
### $first: 0

($first) = @data;      # both sides are in list context   
### $first: 0

$first = shift @data;  # get the first, but @data was modified
### $first: 0
### @data: (1, 2)

($second, $third) = @data;
### $second: 1
### $third: 2

2 Comments

That's good. It would be better if you had a few my keywords in there. And it would be best if it then applied the explanation to the situation of arguments to a subroutine.
Thanks for your suggestion. I removed those should be there my declaration for better s/n ratio, just like most examples of CPAN modules.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.