Reputation: 869
Please explain this apparently inconsistent behaviour:
$a = b, c;
print $a; # this prints: b
$a = (b, c);
print $a; # this prints: c
Upvotes: 4
Views: 314
Reputation: 34120
There is an easy way to see how Perl handles both of the examples, just run them through with:
As you can see, the difference is because the order of operations is slightly different than you might suspect.
perl -MO=Deparse,-p -e'$a = a, b;print $a'
(($a = 'a'), '???');
print($a);
perl -MO=Deparse,-p -e'$a = (a, b);print $a'
($a = ('???', 'b'));
print($a);
Note: you see '???'
, because the original value got optimized away.
Upvotes: 3
Reputation: 20280
Since eugene and mugen have answered this question nicely with good examples already, I am going to setup some concepts then ask some conceptual questions of the OP to see if it helps to illuminate some Perl concepts.
The first concept is what the sigils $
and @
mean (we wont descuss %
here). @
means multiple items (said "these things"). $
means one item (said "this thing"). To get first element of an array @a
you can do $first = $a[0]
, get the last element: $last = $a[-1]
. N.B. not @a[0]
or @a[-1]
. You can slice by doing @shorter = @longer[1,2]
.
The second concept is the difference between void, scalar and list context. Perl has the concept of the context in which your containers (scalars, arrays etc.) are used. An easy way to see this is that if you store a list (we will get to this) as an array @array = ("cow", "sheep", "llama")
then we store the array as a scalar $size = @array
we get the length of the array. We can also force this behavior by using the scalar
operator such as print scalar @array
. I will say it one more time for clarity: An array (not a list) in scalar context will return, not an element (as a list does) but rather the length of the array.
Remember from before you use the $
sigil when you only expect one item, i.e. $first = $a[0]
. In this way you know you are in scalar context. Now when you call $length = @array
you can see clearly that you are calling the array in scalar context, and thus you trigger the special property of an array in list context, you get its length.
This has another nice feature for testing if there are element in the array. print '@array contains items' if @array; print '@array is empty' unless @array
. The if/unless tests force scalar context on the array, thus the if sees the length of the array not elements of it. Since all numerical values are 'truthy' except zero, if the array has non-zero length, the statement if @array
evaluates to true and you get the print statement.
Void context means that the return value of some operation is ignored. A useful operation in void context could be something like incrementing. $n = 1; $n++; print $n;
In this example $n++
(increment after returning) was in void context in that its return value "1" wasn't used (stored, printed etc).
The third concept is the difference between a list and an array. A list is an ordered set of values, an array is a container that holds an ordered set of values. You can see the difference for example in the gymnastics one must do to get particular element after using sort
without storing the result first (try pop sort { $a cmp $b } @array
for example, which doesn't work because pop does not act on a list, only an array).
Now we can ask, when you attempt your examples, what would you want Perl to do in these cases? As others have said, this depends on precedence.
In your first example, since the =
operator has higher precedence than the ,
, you haven't actually assigned a list to the variable, you have done something more like ($a = "b"), ("c")
which effectively does nothing with the string "c". In fact it was called in void context. With warnings enabled, since this operation does not accomplish anything, Perl attempts to warn you that you probably didn't mean to do that with the message: Useless use of a constant in void context
.
Now, what would you want Perl to do when you attempt to store a list to a scalar (or use a list in a scalar context)? It will not store the length of the list, this is only a behavior of an array. Therefore it must store one of the values in the list. While I know it is not canonically true, this example is very close to what happens.
my @animals = ("cow", "sheep", "llama");
my $return;
foreach my $animal (@animals) {
$return = $animal;
}
print $return;
And therefore you get the last element of the list (the canonical difference is that the preceding values were never stored then overwritten, however the logic is similar).
There are ways to store a something that looks like a list in a scalar, but this involves references. Read more about that in perldoc perlreftut
.
Hopefully this makes things a little more clear. Finally I will say, until you get the hang of Perl's precedence rules, it never hurts to put in explicit parentheses for lists and function's arguments.
Upvotes: 3
Reputation: 4429
As eugene's answer seems to leave some questions by OP i try to explain based on that:
$a = "b", "c";
print $a;
Here the left argument is $a = "b"
because =
has a higher precedence than ,
it will be evaluated first. After that $a
contains "b"
.
The right argument is "c"
and will be returned as i show soon.
At that point when you print $a
it is obviously printing b
to your screen.
$a = ("b", "c");
print $a;
Here the term ("b","c")
will be evaluated first because of the higher precedence of parentheses. It returns "c"
and this will be assigned to $a
.
So here you print "c"
.
$var = ($a = "b","c");
print $var;
print $a;
Here $a
contains "b" and $var
contains "c".
Once you get the precedence rules this is perfectly consistent
Upvotes: 8
Reputation: 149736
The =
operator has higher precedence than ,
.
And the comma operator throws away its left argument and returns the right one.
Note that the comma operator behaves differently depending on context. From perldoc perlop:
Binary "," is the comma operator. In scalar context it evaluates its left argument, throws that value away, then evaluates its right argument and returns that value. This is just like C's comma operator.
In list context, it's just the list argument separator, and inserts both its arguments into the list. These arguments are also evaluated from left to right.
Upvotes: 16