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.
Related
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.
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
print (a..c) # this prints: abc
print ($a = "abc") # this prints: abc
print ($a = a..c); # this prints: 1E0
I would have thought it would print: abc
use strict;
print ($a = "a".."c"); # this prints 1E0
Why? Is it just my computer?
edit: I've got a partial answer (the range operator .. returns a boolean value in scalar context - thanks) but what I don't understand is:
why does: print ($a = "a"..."c") produce 1 instead of 0
why does: print ($a = "a".."c") produce 1E0 instead of 1 or 0
There are a number of subtle things going on here. The first is that .. is really two completely different operators depending on the context in which it's called. In list context it creates a list of values (incrementing by one) between the given starting and ending points.
#numbers = 1 .. 3; # 1, 2, 3
#letters = 'a' .. 'c'; # a, b, c (Yes, Perl can increment strings)
Because print interprets its arguments in list context
print 'a' .. 'c'; # <-- this
print 'a', 'b', 'c'; # <-- is equivalent to this
In scalar context, .. is flip-flop operator. From Range Operators in perlop:
It is false as long as its left operand is false. Once the left
operand is true, the range operator stays true until the right operand
is true, AFTER which the range operator becomes false again.
Assignment to a scalar value as in $a = ... creates scalar context. That means that the .. in print ($a = 'a' .. 'c') is an instance of the flip-flop operator, not the list creation operator.
The flip-flop operator is designed to be used when filtering lines in a file. e.g.
while (<$fh>) {
print if /first/ .. /last/;
}
would print all of the lines in a file starting with the one that contained first and ending with the one that contained last.
The flip-flop operator has some additional magic designed to make it easy to filter based on the line number.
while (<$fh>) {
print if 10 .. 20;
}
will print lines 10 through 20 of a file. It does this by employing special case behavior:
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 strings a and c are both constant expressions so they trigger this special case. They aren't numbers, but they're used as numbers (== is a numeric comparison). Perl will convert scalar values between strings and numbers as needed. In this case, both values nummify to 0. Therefore
print ($a = 'a' .. 'c'); # <-- this
print ($a = 0 .. 0); # <-- is effectively this
print ($a = ($. == 0) .. ($. == 0)); # <-- which is really this
We're getting close to the bottom of the mystery. On to the next bit. More from perlop:
The value returned is either the empty string for false, or a sequence
number (beginning with 1) for true. The sequence number is reset for
each range encountered. The final sequence number in a range has the
string "E0" appended to it
If you haven't read any lines from a file yet, $. will be undef which is 0 in a numerical context. 0 == 0 is true, so the .. returns a true value. It's the first true value, so it's 1. Because both the left-hand and right-hand sides are true the first true value is also the last true value and the E0 "this is the last value" suffix is appended to the return value. That is why print ($a = 'a' .. 'c') prints 1E0. If you were to set $. to a non-zero value the .. would be false and return the empty string.
print ($a = 'a' .. 'c'); # prints "1E0"
$. = 1;
print ($a = 'a' .. 'c'); # prints nothing
The very final piece of the puzzle (and I might be going too far now) is that the assignment operator returns a value. In this case that's the value assigned to $a1 -- 1E0. This value is what is ultimately spit out by the print.
1: Technically, the assignment produces a lvalue for the item assigned to. i.e. it returns an lvalue for the variable $a which then evaluates to 1E0.
It's a matter of list context vs. scalar context, as explained in perldoc perlop:
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. Each ".." operator
maintains its own boolean state, even across calls to a subroutine
that contains it. It is false as long as its left operand is false.
Once the left operand is true, the range operator stays true until the
right operand is true, AFTER which the range operator becomes false
again. It doesn't become false till the next time the range operator
is evaluated. It can test the right operand and become false on the
same evaluation it became true (as in awk), but it still returns true
once. If you don't want it to test the right operand until the next
evaluation, as in sed, just use three dots ("...") instead of two. In
all other regards, "..." behaves just like ".." does.
[snip]
The final sequence number in a range has the string "E0" appended to
it, which doesn't affect its numeric value, but gives you something to
search for if you want to exclude the endpoint.
EDIT in response to DanD man's comment:
I find it a bit hard to digest too; frankly, I rarely use the .. operator, and even more rarely in scalar context. But for example, the expression 5..10 in an input loop implicitly compares to the current value of $. (that's part of the description that I didn't quote; see the manual). On lines 5 through 9, it yields a true value (experiment shows that it's a number, but the documentation doesn't say so). On line 10, it yields a number with "E0" appended to it -- i.e., it's in exponential notation, but with the same value it would have without the "E0".
The point of the "E0" tweak is to let you detect whether you're in a specified range and to flag the last line in the range for special treatment. Without the "E0", you wouldn't be able to treat the final match specially.
An example:
#!/usr/bin/perl
use strict;
use warnings;
while (<>) {
my $dotdot = 2..4;
print "On line $., 2..4 yields \"$dotdot\"\n";
}
Given 5 lines of input, this prints:
On line 1, 2..4 yields ""
On line 2, 2..4 yields "1"
On line 3, 2..4 yields "2"
On line 4, 2..4 yields "3E0"
On line 5, 2..4 yields ""
This lets you detect whether a line is inside or outside the range and when it's the last line in the range.
But scalar .. is probably more commonly used just for its boolean result, often in one-liners; for example, perl -ne 'print if 2..4' will print lines 2, 3, and 4 of whatever input you give it. It's deliberately similar to sed -n '2,4p'.
The answer can be found by consulting perldoc's perlop page:
Binary ".." is the range operator, which is really two different operators depending on the context. In list context, it returns a list of values counting (up by ones) from the left value to the right value...
This is the familiar usage, which is invoked by print "a" .. "c"; because arguments to functions are evaluated in list context. (If they were evaluated in scalar context, then print #list would print the size of #list, which is almost definitely not what people usually want.)
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. Each ".." operator maintains its own boolean state, even across calls to a subroutine that contains it. It is false as long as its left operand is false. Once the left operand is true, the range operator stays true until the right operand is true, AFTER which the range operator becomes false again. It doesn't become false till the next time the range operator is evaluated. It can test the right operand and become false on the same evaluation it became true (as in awk), but it still returns true once. If you don't want it to test the right operand until the next evaluation, as in sed, just use three dots ("...") instead of two. In all other regards, "..." behaves just like ".." does.
It goes into further detail, but the bolded sections are the important parts to understanding how the operator works. Scalar context is forced by $a =, i.e. assignment to a scalar lvalue. If you did #a =, it would print what you expect.
Note that "a" .. "b" doesn't produce the string "abc", it produces the list ("a", "b", "c"). You will get similar results if you used the list (though the value printed when the list is forced into scalar context will differ).
$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.
Please explain this apparently inconsistent behaviour:
$a = b, c;
print $a; # this prints: b
$a = (b, c);
print $a; # this prints: c
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.
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
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.
There is an easy way to see how Perl handles both of the examples, just run them through with:
perl -MO=Deparse,-p -e'...'
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.