Perl5 = (equals) operator precedence - perl

$a,$b,$c = 1,2,3;
print "$a, $b, $c\n";
returns
, , 1
So does = (equals) take higher precedence than the tuple construction - doing this?
$a,$b,($c=1),2,3;

Yes. There's a precedence table in perlop. Assignment operators are level 19, and comma is level 20. In general, Perl's operators have the same precedence as the corresponding C operators (for those operators that have a corresponding C operator).
If you meant ($a,$b,$c) = (1,2,3); you have to use the parens.

The comma operator as you used it (in scalar context) is not for tuple construction, it's for evaluating several expressions and returning the last one.
Perl does things differently depending on context, it decides what to do depending on if it's expecting a scalar value, a list, nothing at all, ... See perldoc perldata's section on Context for an introduction.
So, if you do:
perl -e '$a = (1 and 4,2,0); print"$a\n"'
You get 0, because 4,2,0 is evaluated in scalar context, and behaves like C's comma operator, evaluating expressions between commas and returning the result of the last one.
If you force 4,2,0 to be evaluated in list context:
perl -e '$a = (1 and #a=(4,2,0)); print"$a\n"'
You get 3, because assigning to an array forces list context (the additional parenthesis are there to solve the precedence issue cjm mentioned), and the value of a list in scalar context (forced by being the RHS of an and in scalar context) is the number of elements it has (logical and in Perl returns the last expression evaluated, instead of a boolean value as in other programming languages).
So, as cjm said, you need to do:
($a,$b,$c) = (1,2,3);
to deal with precedence and force list context.
Notice the difference between:
$ perl -e '$a,$b,$c = (7,6,8); print "$a $b $c\n"'
8
The comma operator is evaluated in scalar context, and returns 8.
$ perl -e '($a,$b,$c) = (7,6,8); print "$a $b $c\n"'
7 6 8
The comma operator is evaluated in list context, and returns a list.
$ perl -e '$a,$b,$c = () = (7,6,8); print "$a $b $c\n"'
3
The comma operator is evaluated in list context, returning a list, then the assignment to $c forces scalar context, returning the number of elements in the list.

Related

Scalar vs list context

Wonder what is the rationale behind following two examples giving different results when in both cases do {} returns lists.
perl -wE 'say my $r = do { (44); }'
44
perl -wE 'say my $r = do { my ($x) = map $_, 44; }'
1
In both cases the assignment to $r is forcing scalar context on the do. However in the first case scalar context on a list returns the last value of the list, '44'.
In the second instance the assignment to my ($x) forces a list context, The result of assigning to a list in scalar context is the number of elements on the right hand side of the assignment. So you get.
map $_, 44 returns a list of length 1 containing (44)
my ($x) = assigns the results above in list context, because of the brackets around $x, to the list ($x) making $x = 44
The do block is in scalar context because of the assignment to $r, note the lack of brackets, and as I said above this returns the length of the right hand side of the list assignment. 1 in this case.
See what happens if you do this:
perl -wE 'say my $r = () = (1,3,5,7)'
First of all, neither do returns a list. They are evaluated in scalar context so they must return a single scalar, not an arbitrary number of scalars ("a list").
In the first case, the do returns the result of evaluating 44 in scalar context. This returns 44.
scalar( 44 ) ⇒ 44
In the second case, the do returns the result of evaluating a list assignment in scalar context. This returns the number of elements returned by the right-hand side of the assignment.
scalar( () = 44 ) ⇒ 1
I believe the real cause of your confusion is that you don't know about the assignment operators and how they are affected by context. If so, see Scalar vs List Assignment Operator for the answer to your real question.

What is the Perl context with range operator?

I'm a Perl newbie. I want to understand Perl context in conjunction with range operator. This is my code.
use strict;
use warnings;
my $asc = ( 10 .. 50 );
print "$asc\n";
I have two doubts.
If the expression ( 10 .. 50 ) returns an array, then, as it's a scalar context, "asc" variable should be assigned the length of the array, that is, 41.
If expression ( 10 ..50 ) returns a list, then, as it's a scalar context, "asc" variable should be assigned the last item from the list, that is, 50.
But, I get the following shout ..
Use of uninitialized value in range (or flip) at main.pl line ..
Appreciate and welcome any guide.
You're working with the Range Operator .. in an scalar context, which is otherwise known as the flip-flop operator.
You should read the entire documentation, but the following excerpts are relevant to your situation:
In scalar context, ".." returns a boolean value. The operator is bistable, like a flip-flop, and emulates the line-range (comma) operator of sed, awk, and various editors.
...
If either operand of scalar ".." is a constant expression, that operand is considered true if it is equal (==) to the current input line number (the $. variable).
The “exact“ error message explains what's going on:
Use of uninitialized value $. in range (or flip)
Basically, Perl interprets this usage as a flip/flop test.
It's testing if the current line number $. is equal to the integer values you specified:
my $asc = ( $. == 10 .. $. == 50 );
However, because you haven't read from a file handle, the $. variable is uninitialized and throws a warning.
Achieving a List Context
It is possible to get the list context behavior that you described, but you'll need to adjust the code to make your intent more explicit:
my $count = () = (10..50); # Forces a list context
my $last_element = (10..50)[-1]; # Also forces a list context
print "$count\n";
print "$last_element\n";
Outputs:
41
50
If the expression ( 10 .. 50 ) returns an array, then, as it's a scalar context, ...
If expression ( 10 ..50 ) returns a list, then, as it's a scalar context, ...
You're operating on some very incorrect misconceptions.
It's impossible to return an array[1]. The only thing that can be returned is zero or more scalars. In scalar context, that list must be exactly one scalar long.
Scalar context causes the operator to change what it returns; it doesn't cause the returned value to be coerced into a scalar.
Each operator decides what it returns in each context. Some, like the range operator (..), even change behaviour based on context. In fact, the behaviour of the range operator in scalar context is significantly different than its behaviour in list context. So much so, that's it's usually called by a different name when it's in scalar context: the flip-flop operator.
This question isn't really about the flip-flop operator, so I'm not going to go into too much detail. (The documentation for it is here.) Suffice it to say it can be used to selectively print lines by number with very little code.
For example,
while (<DATA>) {
print if 2..3;
}
__DATA__
aaa
bbb
ccc
ddd
eee
outputs
bbb
ccc
Part of the "magic" involve comparing the numbers provided against $., the line number of the last line read. That means that
my $ac = 10..50;
is short for
my $ac = ($. == 10) .. ($. == 50);
Since you never read from a file, $. is undefined.
When you do return #array;, you either return the elements of the array ($array[0], $array[1], ...) or its length depending on context.

Why do I get the last value in a list in scalar context in perl?

I had assumed that, in perl, $x=(2,3,4,5) and ($x)=(2,3,4,5) would give me the same result, but was surprised at what happened in my tests. I am wondering why this behavior is the way it is and why wantarray behaves differently. Here are my tests and the results:
>perl -e '$x=(1,2,3,5);print("$x\n")'
5
>perl -e '($x)=(1,2,3,5);print("$x\n")'
1
>perl -e '$x=(wantarray ? (1,2,3,5) : 4);print("$x\n")'
4
>perl -e '($x)=(wantarray ? (1,2,3,5) : 4);print("$x\n")'
4
Is this behavior consistent/reliable across all platforms?
Whoops. wantarray is for context of subroutine calls...
>perl -e '$x=test();sub test{return(1,2,3,5)};print("$x\n")'
5
>perl -e '($x)=test();sub test{return(1,2,3,5)};print("$x\n")'
1
>perl -e '$x=test();sub test{return(wantarray ? (1,2,3,5) : 4)};print("$x\n")'
4
>perl -e '($x)=test();sub test{return(wantarray ? (1,2,3,5) : 4)};print("$x\n")'
1
So I guess it is consistent, but why does the list return the last value in scalar context?
So I guess it is consistent, but why does the list return the last value in scalar context?
Because it's useful.
my $x = f() || ( warn('!!!'), 'default' );
Well, more useful than any other alternative, at least. It's also consistent with its stronger cousin, ;.
sub f { x(), y(), z() };
is the same as
sub f { x(); y(); z() };
Each operator determines decides what it returns in both scalar and list context.[1]
There are operators that only even return a single scalar.
say time; # Returns the number of seconds since epoch
say scalar( time ); # Ditto.
But operators that normally returns more than one scalar and those that return a variable number of scalars cannot possible return that in scalar context, so they will return something else. It's up to each one to decide what that is.
say scalar( 4,5,6 ); # Last item evaluated in scalar context.
say scalar( #a ); # Number of elements in #a.
say scalar( grep f(), g() ); # Number of matching items.
say scalar( localtime ); # Formatted timestamp.
The list operator (e.g. x,y,z) returns what the last item of the list (z) returns when evaluated in scalar context. For example,
my $x = (f(),g(),#a);
is a weird way of writing
f();
g();
my $x = #a;
Notes
Same goes for subs, though it's common to write subs that are useless to call in scalar context.
You have two types of assigment.
The first one is in scalar context and that is how the comma operators behave in this context (it evaluates both arguments but discard the one to the left and return the value to the right).
The second one is list context (when you enclose a variable in parentheses you are asking for list context). In list context the comma operator just separates the arguments and returns the whole list.
From the Documentation
Comma Operator
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.
http://perldoc.perl.org/perlop.html#Comma-Operator

How exactly does Perl handle operator chaining?

So I have this bit of code that does not work:
print $userInput."\n" x $userInput2; #$userInput = string & $userInput2 is a integer
It prints it out once fine if the number is over 0 of course, but it doesn't print out the rest if the number is greater than 1. I come from a java background and I assume that it does the concatenation first, then the result will be what will multiply itself with the x operator. But of course that does not happen. Now it works when I do the following:
$userInput .= "\n";
print $userInput x $userInput2;
I am new to Perl so I'd like to understand exactly what goes on with chaining, and if I can even do so.
You're asking about operator precedence. ("Chaining" usually refers to chaining of method calls, e.g. $obj->foo->bar->baz.)
The Perl documentation page perlop starts off with a list of all the operators in order of precedence level. x has the same precedence as other multiplication operators, and . has the same precedence as other addition operators, so of course x is evaluated first. (i.e., it "has higher precedence" or "binds more tightly".)
As in Java you can resolve this with parentheses:
print(($userInput . "\n") x $userInput2);
Note that you need two pairs of parentheses here. If you'd only used the inner parentheses, Perl would treat them as indicating the arguments to print, like this:
# THIS DOESN'T WORK
print($userInput . "\n") x $userInput2;
This would print the string once, then duplicate print's return value some number of times. Putting space before the ( doesn't help since whitespace is generally optional and ignored. In a way, this is another form of operator precedence: function calls bind more tightly than anything else.
If you really hate having more parentheses than strictly necessary, you can defeat Perl with the unary + operator:
print +($userInput . "\n") x $userInput2;
This separates the print from the (, so Perl knows the rest of the line is a single expression. Unary + has no effect whatsoever; its primary use is exactly this sort of situation.
This is due to precedence of . (concatenation) operator being less than the x operator. So it ends up with:
use strict;
use warnings;
my $userInput = "line";
my $userInput2 = 2;
print $userInput.("\n" x $userInput2);
And outputs:
line[newline]
[newline]
This is what you want:
print (($userInput."\n") x $userInput2);
This prints out:
line
line
As has already been mentioned, this is a precedence issue, in that the repetition operator x has higher precedence than the concatenation operator .. However, that is not all that's going on here, and also, the issue itself comes from a bad solution.
First off, when you say
print (($foo . "\n") x $count);
What you are doing is changing the context of the repetition operator to list context.
(LIST) x $count
The above statement really means this (if $count == 3):
print ( $foo . "\n", $foo . "\n", $foo . "\n" ); # list with 3 elements
From perldoc perlop:
Binary "x" is the repetition operator. In scalar context or if the left operand is not enclosed in parentheses, it returns a string consisting of the left operand repeated the number of times specified by the right operand. In list context, if the left operand is enclosed in parentheses or is a list formed by qw/STRING/, it repeats the list. If the right operand is zero or negative, it returns an empty string or an empty list, depending on the context.
The solution works as intended because print takes list arguments. However, if you had something else that takes scalar arguments, such as a subroutine:
foo(("text" . "\n") x 3);
sub foo {
# #_ is now the list ("text\n", "text\n", "text\n");
my ($string) = #_; # error enters here
# $string is now "text\n"
}
This is a subtle difference which might not always give the desired result.
A better solution for this particular case is to not use the concatenation operator at all, because it is redundant:
print "$foo\n" x $count;
Or even use more mundane methods:
for (0 .. $count) {
print "$foo\n";
}
Or
use feature 'say'
...
say $foo for 0 .. $count;

How does Perl parse print " " . ($x, $y) ."\n"?

I know from Learning Perl, 6th Ed. (ISBN: 978-1-449-30358-7) p.58 that ($x, $y) = "something", "new"; is a list context. So why does the following code print " bee"? Please explain how does the code parsed.
$dina = bobba;
$ba = bee;
print " " . ($dina, $ba)."\n";
The concatenation operator . imposes scalar context on the list created by the comma operator, so it returns its last member.
The most relevant documentation quote is this paragraph from perlop(1):
Comma Operator
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.
"($x, $y) = ("something", "new"); is a list context." makes no sense. (Added the missing parenthesis to avoid going off-topic.)
First, something is evaluated in list context.
Second, there's no way to know in which context that expression will be evaluated from what you posted, but chances are it's evaluated in void context.
You are probably referring to the sub expressions ($x, $y) and ("something", "new"). They are evaluated indeed evaluated in list context, and that's because the list assignment operator evaluates its operands in list context.
In your code, ($x, $y) is the operand of a concatenation operator (.). The concatenation operator combines two strings, so it expects strings as operands. Strings being scalars, the concatenation operator evaluates its operands in scalar context.
In scalar context,
$x, $y
is about the same as
do { $x; $y }
(without the additional scope). Each item of the list is evaluated in turn in void or scalar context, and the whole evaluates to what the last item in the list returned.
>perl -E"sub f { say 'f'; 3 } sub g { say 'g'; 4 } say ':'.(f,g);"
f
g
:4