= and , operators in Perl - perl

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.

Related

why perl doesn't recognize i want to create an arrayRef with "split"?

I want to split a scalar by whitespaces and save the result in an ArrayReference.
use strict;
use warnings;
use Data::Dumper;
my $name = 'hans georg mustermann';
my $array = split ' ', $name;
print Dumper($array); #$VAR1 = 3;
So it seems $array is now a scalar with the size resulted by the split operation.
When i change the code to my $array = [split ' ', $name]; the variable $array is now a ArrayReference and contains all 3 strings.
I just don't understand this behavior. Would be really great if someone could explain it to me or post a good documentation about these things, as i don't know how to search for this topic.
Thank you in advance
What you see here is called "context". The documentation about this is rather scattered. You also want to take a look at this tutorial about "scalar vs list context" https://perlmaven.com/scalar-and-list-context-in-perl
If you assign the result of split (or any subroutine calls) to an array, it's list context:
my #arr = split ' ', $name;
#=> #arr = ('hans', 'georg', 'mustermann');
What your example code shows is assigning them to a scalar -- and therefore it's under "scalar context".
Since, naturally, multiple things cannot fit into one position, some sort of summarization needs to be done. In the case of split function, perl5 has defined that the number of elements in the result of split shall be the best.
Check the documentation of the split function: https://perldoc.pl/functions/split -- which actually defines the behaviour under scalar context as well as list context.
Also take a glance at the documentation of all builtin functions at https://perldoc.pl/functions -- you'll find the behaviour definition under "list context" and "scalar context" for most of them -- although many of them are not returning "the size of lists" but rather something else.
That's called context.
The partial expression split ' ', $name evaluates to a list. The partial expression $array = LIST coerces the list to a scalar value, namely counting the number of elements in the list. That's the default behaviour of lists in scalar context.
You should write #array = LIST instead, using an array variable, not a scalar variable, in order to preserve the list values.
If you read the documentation for split(), you'll find the bit that explains what the function returns.
Splits the string EXPR into a list of strings and returns the list in list context, or the size of the list in scalar context.
You're calling the function in scalar context (because you're assigning the result of the call to a scalar variable) so you're getting the size of the list.
If you want to get the list, then you need to store it either in a list of variables:
my ($forename, $middlename, $surname) = split ' ', $name;
Or (more usually) in an array:
my #name_parts = split ' ', $name;
But actually, you say that you want an array reference. You can do that by calling split() inside an anonymous array constructor ([ ... ]) and assigning the result of that call to a scalar variable.
my $name_parts = [ split ' ', $name ];

perl assign reference to subroutine

I use #_ in a subroutine to get a parameter which is assigned as a reference of an array, but the result dose not showing as an array reference.
My code is down below.
my #aar = (9,8,7,6,5);
my $ref = \#aar;
AAR($ref);
sub AAR {
my $ref = #_;
print "ref = $ref";
}
This will print 1 , not an array reference , but if I replace #_ with shift , the print result will be a reference.
can anyone explain why I can't get a reference using #_ to me ?
This is about context in Perl. It is a crucial aspect of the language.
An expression like
my $var = #ary;
attempts to assign an array to a scalar.
That doesn't make sense as it stands and what happens is that the right-hand side is evaluated to the number of elements of the array and that is assigned to $var.
In order to change that behavior you need to provide the "list context" to the assignment operator.† In this case you'd do
my ($var) = #ary;
and now we have an assignment of a list (of array elements) to a list (of variables, here only $var), where they are assigned one for one. So here the first element of #ary is assigned to $var. Please note that this statement plays loose with the elusive notion of the "list."
So in your case you want
my ($ref) = #_;
and the first element from #_ is assigned to $ref, as needed.
Alternatively, you can remove and return the first element of #_ using shift, in which case the scalar-context assignment is fine
my $ref = shift #_;
In this case you can also do
my $ref = shift;
since shift by default works on #_.
This is useful when you want to remove the first element of input as it's being assigned so that the remaining #_ is well suited for further processing. It is often done in object-oriented code.
It is well worth pointing out that many operators and builtin facilities in Perl act differently depending on what context they are invoked in.
For some specifics, just a few examples: the regex match operator returns true/false (1/empty string) in scalar context but the actual matches in list context,‡ readdir returns a single entry in scalar context but all of them in list context, while localtime shows a bit more distinct difference. This context-sensitive behavior is in every corner of Perl.
User level subroutines can be made to behave that way via wantarray.
†
See Scalar vs List Assignment Operator
for a detailed discussion
‡
See it in perlretut and in perlop for instance
When you assign an array to a scalar, you're getting the size of the array. You pass one argument (a reference to an array) to AAR, that's why you get 1.
To get the actual parameters, place the local variable in braces:
sub AAR {
my ($ref) = #_;
print "ref = $ref\n";
}
This prints something like ref = ARRAY(0x5566c89a4710).
You can then use the reference to access the array elements like this:
print join(", ", #{$ref});

Perl: If two elements match print elements, else iterate until match and then print

I'm new to Perl and I'm trying to iterate over two elements of an array with multiple indices in each element and look for a match. If element2 matches element1, I want to print both and move to the next position in element1 and continue the loop looking for the next match. If I don't have a match, loop until I get a match. Here is what I have:
#array = split(',',$row);
foreach $element1(#array[1])
{
foreach $element2(#array[2])
{
if($element1 == $element2)
{
print "1 = $element1 : 2 = $element2 \n";
}
}
}
I'm not getting the the matched output. I've tried multiple iterations with different syntactical changes.
I can get both elements when I do this:
foreach $element1(#array[1])
{
foreach $element2(#array[2])
{
print "1 = $element1 : 2 = $element2 \n";
}
}
I thought I might not be dereferencing correctly. Any guidance or suggestions would be appreciated. Thanks.
There are a number of issues with your script. Briefly:
You should always use strict and warnings.
Array indices start at 0, not 1.
You get an element of an array with $array[0], not #array[0]. This is a common frustration for new Perl programmers. The thing to remember is that the sigil (the symbol preceding a variable name) indicates the type of value being passed (e.g. $scalar, #array, or %hash) to the left-hand side of the expression, not the type of datastructure being accessed on the right-hand side.
As #sp-asic pointed out in the comments on the OP, string comparisons are performed with eq, not ==.
References to datastructures are stored in scalars, and you dereference by prepending the sigil of the original datastructure. If $foo is a reference to an array, #$foo gets you the original array.
You apparently want to break out of your inner loops when you find a match, but you'll want to make it clear (for people who look at this code in the future, which may include yourself) which loop you're breaking out of.
Most critically, #array will be an array of strings after you split another string (the row) on commas, so it's not clear why you expect to be able to treat the strings in the first and second position as arrays that you can loop through. I have a few guesses about what you're actually trying to do, and what your inputs and expected outputs actually look like, but I'll wait for you to provide some additional information and leave the information above as general guidance in the meantime, along with a lightly-reworked version of your code below.
use strict;
use warnings;
my #array = split(',', $row);
foreach my $element1 (#$array[0]) {
foreach my $element2 (#$array[1]) {
if ($element1 eq $element2) {
print "1 = $element1 : 2 = $element2\n";
last;
}
}
}

Perl "reverse comma operator" (Example from the book Programming Perl, 4th Edition)

I'm reading "Programming Perl" and ran into a strange example that does not seem to make sense. The book describes how the comma operator in Perl will return only the last result when used in a scalar context.
Example:
# After this statement, $stuff = "three"
$stuff = ("one", "two", "three");
The book then gives this example of a "reverse comma operator" a few pages later (page 82)
# A "reverse comma operator".
return (pop(#foo), pop(#foo))[0];
However to me this doesn't seem to be reverse at all..?
Example:
# After this statement, $stuff = "three"
$stuff = reverse_comma("one", "two", "three");
# Implementation of the "reverse comma operator"
sub reverse_comma {
return (pop(#_), pop(#_))[0];
}
How is this in any way reverse of the normal comma operator? The results are the same, not reversed!
Here is a link to the exact page. The example is near the bottom.
It's a bad example, and should be forgotten.
What it's demonstrating is simple:
Normally, if you have a sequence of expressions separated by commas in scalar context, that can be interpreted an instance of the comma operator, which evaluates to the last thing in the sequence.
However, if you put that sequence in parentheses and stick [0] at the end, it turns that sequence into a list and takes its first element, e.g.
my $x = (1, 2, 3)[0];
For some reason, the book calls this the "reverse comma operator". This is a misnomer; it's just a list that's having its first element taken.
The book is confusing matters further by using the pop function twice in the arguments. These are evaluated from left to right, so the first pop evaluates to "three" and the second one to "two".
In any case: Don't ever use either the comma or "reverse comma" operators in real code. Both are likely to prove confusing to future readers.
It's a cute and clever example, but thinking too hard about it distracts from its purpose. That section of the book is showing off list slices. Anything beyond slicing a list, no matter what is in the list, is not germane to the purpose of the examples.
You're only on page 82 of a very big book (we literally couldn't fit any more pages in because we were at the limit of the binding method), so there's not much we could throw at you. Among the other list slices examples, there this clever one that I wouldn't use in real code. That's the curse of contrived examples though.
But let's say there were a reverse comma operator. It would have to evaluate both side of the comma. Many answers go right for "just return the first thing". That's not the feature though. You have to visit every expression even though you keep one of them.
Consider this much much advanced version with a series of anonymous subroutines that I immediately dereference, each of which prints something then returns a result:
use v5.10;
my $scalar = (
sub { say "First"; 35 } -> (),
sub { say "wantarray is ", 0+wantarray } -> (),
sub { say "Second"; 27 } -> (),
sub { say "Third"; 137 } -> ()
);
The parens are there only for precedence since the assignment operator binds more tightly than the comma operator. There's no list here, even though it looks like there is one.
The output shows that Perl evaluated each even though it kept on the last one:
First
wantarray is 0
Second
Third
Scalar is [137]
The poorly-named wantarray built-in returns false, noting that the subroutine thinks it is in scalar context.
Now, suppose that you wanted to flip that around so it still evaluates every expression but keeps the first one. You can use a literal list access:
my $scalar = (
sub { say "First"; 35 } -> (),
sub { say "wantarray is ", 0+wantarray } -> (),
sub { say "Second"; 27 } -> (),
sub { say "Third"; 137 } -> ()
)[0];
With the addition on the subscription, the righthand side is now a list and I pull out the first item. Notice that the second subroutine thinks it is in list context now. I get the result of the first subroutine:
First
wantarray is 1
Second
Third
Scalar is [35]
But, let's put this in a subroutine. I still need to call each subroutine even though I won't use the results of the other ones:
my $scalar = reverse_comma(
sub { say "First"; 35 },
sub { say "wantarray is ", 0+wantarray },
sub { say "Second"; 27 },
sub { say "Third"; 137 }
);
say "Scalar is [$scalar]";
sub reverse_comma { ( map { $_->() } #_ )[0] }
Or, would I use the results of the other ones? What if I did something slightly different. I'll add a side effect of setting $last to the evaluated expression:
use v5.10;
my $last;
my $scalar = reverse_comma(
sub { say "First"; 35 },
sub { say "wantarray is ", 0+wantarray },
sub { say "Second"; 27 },
sub { say "Third"; 137 }
);
say "Scalar is [$scalar]";
say "Last is [$last]";
sub reverse_comma { ( map { $last = $_->() } #_ )[0] }
Now I see the feature that makes the scalar comma interesting. It evaluates all the expressions, and some of them might have side effects:
First
wantarray is 0
Second
Third
Scalar is [35]
Last is [137]
It's not a huge secret that tchrist is handy with shell scripts. The Perl to csh converter was basically his inbox (or comp.lang.perl). You'll see some shell like idioms popping up in his examples. Something like that trick to swap two numerical values with one statement:
use v5.10;
my $x = 17;
my $y = 137;
say "x => $x, y => $y";
$x = (
$x = $x + $y,
$y = $x - $y,
$x - $y,
);
say "x => $x, y => $y";
The side effects are important there.
So, back to the Camel, we have an example of where the thing that has the side effect is the pop array operator:
use v5.10;
my #foo = qw( g h j k );
say "list: #foo";
my $scalar = sub { return ( pop(#foo), pop(#foo) )[0] }->();
say "list: #foo";
This shows off that each expression on either side of all the commas are evaluated. I threw in the subroutine wrapper since we didn't show a complete example:
list: g h j k
list: g h
But, none of this was the point of that section, which was indexing into a list literal. The point of the example was not to return a different result than the other examples or Perl features. It was to return the same thing assuming the comma operator acted differently. The section is about list slices and is showing off list slices, so the stuff in the list wasn't the important part.
Let's make the examples more similar.
The comma operator:
$scalar = ("one", "two", "three");
# $scalar now contains "three"
The reverse comma operator:
$scalar = ("one", "two", "three")[0];
# $scalar now contains "one"
It's a "reverse comma" because $scalar gets the result of the first expression, where the normal comma operator gives the last expression. (If you know anything about Lisp, it's like the difference between progn and prog1.)
An implementation of the "reverse comma operator" as a subroutine would look something like this:
sub reverse_comma {
return shift #_;
}
An ordinary comma will evaluate its operands and then return the value of the right-hand operand
my $v = $a, $b
sets $v to the value of $b
For the purpose of demonstrating list slices, the Camel is proposing some code that behaves like the comma operator but instead evaluates its operands and then return the value of the left-hand operand
Something like that can be done with a list slice, like this
my $v = ($a, $b)[0]
which sets $v to the value of $a
That's all there is to it really. The book isn't trying to suggest that there should be a reverse comma subroutine, it is simply considering the problem of evaluating two expressions in order and returning the first. The order of evaluation is relevant only when the two expressions have side effects, which is why the example in the book uses pop, which changes the array as well as returning a value
The imaginary problem is this
Suppose I want a subroutine to remove the last two elements of an array, and then return the value of what used to be the last element
Ordinarily that would require a temporary variable, like this
my $last = pop #foo;
pop #foo;
return $last;
But as an example of a list slice the code suggests that this would also work
# A "reverse comma operator".
return (pop(#foo), pop(#foo))[0];
Please understand that this isn't a recommendation. There are a few ways to do this. Another single-statement way is
return scalar splice #foo, -2;
but that doesn't use a list slice, which is the topic of that section of the book. In reality I doubt if the book's authors would propose anything other than the simple solution with the temporary variable. It is purely an example of what a list slice can do
I hope that helps

Indexing directly into returned array in Perl

This question has been asked about PHP both here and here, and I have the same question for Perl. Given a function that returns a list, is there any way (or what is the best way) to immediately index into it without using a temporary variable?
For example:
my $comma_separated = "a,b,c";
my $a = split (/,/, $comma_separated)[0]; #not valid syntax
I see why the syntax in the second line is invalid, so I'm wondering if there's a way to get the same effect without first assigning the return value to a list and indexing from that.
Just use parentheses to define your list and then index it to pull your desired element(s):
my $a = (split /,/, $comma_separated)[0];
Just like you can do this:
($a, $b, $c) = #array;
You can do this:
my($a) = split /,/, $comma_separated;
my $a on the LHS (left hand side) is treated as scalar context. my($a) is list context. Its a single element list so it gets just the first element returned from split.
It has the added benefit of auto-limiting the split, so there's no wasted work if $comma_separated is large.