I am new to Perl, can anyone explain the following scripts for me please:
#!/usr/bin/env perl
use strict;
use warnings;
sub f1($) { my ($v) = #_; print "f1 $v\n"; }
sub f2(#) { my ($v) = #_; print "f2 $v\n"; }
my $s = "ww";
my #a = ("xx", "yy", "zz");
f1 $s; f1 #a; f2 $s; f2 #a;
The output in my computer is :
f1 ww
f1 3
f2 ww
f2 xx # why!!
Can anyone explain why the fourth output is xx? I thought it should be zz, since when array converts to scalar, it should be the last element of array.
No, with a statement such as:
my ($v, $foo, $bar) = #_;
$v will be assigned the first value in the #_ array, $foo the second, and so on. This is because the parentheses impose a list context. Any excess values will be ignored, unless one of your variables is an array, in which case it will slurp all remaining values.
my ($v, #foo, $bar) = #_; # wrong! $bar will never get any value
$v will get the first value, #foo all the rest. $bar will be undefined.
You may be thinking of assignment using a list:
my $v = qw(a b c);
But this is wrong, and will cause an error:
Useless use of a constant (a) in void context at -e line 1.
Useless use of a constant (b) in void context at -e line 1.
This is because the LHS using scalar context, it will (more or less) be similar to:
'a';
'b';
my $v = 'c';
You may notice that if we impose list context by putting $v inside parentheses, we get a different result:
my ($v) = qw(a b c); # $v is now 'a'
ETA: Regarding prototypes:
In f1, what you see is that the array is forced into scalar context because the subroutine expects a scalar argument. That is why f1 with an array prints 3 (the size). When the prototype looks for an array, the array remains in default list context, and the assignment is done as per normal (as described above).
As an extra note: prototypes have a very specific use, to make subroutines behave more like certain built-ins with regard to argument handling. Such as sort { code here } or push #array, $foo.
If this is not what you are after, you should skip prototypes all together and simply write:
sub f1 {
...
}
Documentation here
Perl array behaves in 2 differents flavours, regarding context:
Scalar context:
my $a = #tab; # get the array length
Array context:
my #newTab = #tab; # newTab is a copy of tab
Which can be reworded like this:
# 3 scalars in an array context (see parens) gets the contents of the tab
my ($a,$b,$c) = #tab;
Here, since #tab can be wider than the number of scalars, these scalars are populated from the beginning of the tab (and not from the end). So your code:
my ($a) = #tab;
will echo the first element of the tab
An array can't be converted to a scalar. An array can be evaluated in scalar context, though.
An array evaluated in scalar context does not return the last element of the array. As you saw with f1, it returns the number of elements in the array.
$ perl -E'my #a = qw( xx yy zz ); say #a; say scalar(#a);'
xxyyzz
3
Neither array is being evaluated in scalar context. An argument list expression is evaluated in list context.
f(#a);
is the same as
f($a[0], $a[1], $a[2]);
The RHS of a list assignment in evaluated in list context.
my ($v) = #_;
is the same as
my ($v) = ($_[0], $_[1], $_[2]);
which is the same as
my ($v, undef, undef) = ($_[0], $_[1], $_[2]);
Related
use strict;
use warnings;
use Test::More;
subtest 'explicit array' => sub {
my #row = (1,2,3);
# let's disassamble the array.
# without default it works:
my ($one, $two, $three) = #row;
is($one, 1, 'one');
# this works too:
($one, $two, $three) = #row ? #row : (10,20,30);
is($one, 1, 'one');
# and the default hits
my #emptyness;
($one, $two, $three) = #emptyness ? #emptyness : (10,20,30);
is($one, 10, 'one default');
# however, this squashes the array to a scalar
($one, $two, $three) = #row // (10,20,30);
is($one, 3, 'scalar, length');
is($two, undef, 'nothing else');
# shouldn't 'defined-or' be equivalent to a ternary with a check against undef?
# ($one, $two, $three) = defined #emptyness ? #emptyness : (10,20,30); # fails!
# "Can't use 'defined(#array)' (Maybe you should just omit the defined()?)"
# Probably #array // ... should fail in the same way, but instead it returns #array
# in a scalar context.
# so maybe this is a bug
};
done_testing();
Or can anybody give me a reasonable explanation for this behavior?
The behavior you are observing is the intended one. This is documented in perlop, in the section Logical Defined-Or:
EXPR1 // EXPR2 returns the value of EXPR1 if it's defined, otherwise, the value of EXPR2 is returned. (EXPR1 is evaluated in scalar context, EXPR2 in the context of // itself).
And, perldoc later provides the following example:
In particular, this means that you shouldn't use this for selecting between two aggregates for assignment:
#a = #b || #c; # This doesn't do the right thing
#a = scalar(#b) || #c; # because it really means this.
#a = #b ? #b : #c; # This works fine, though.
// must evaluate its left-hand side operator in scalar context since it requires a scalar to evaluate for defined-ness.
#a returns the number of elements the array contains in scalar context.
So #a // ... always returns the number of elements in #a (since all numbers are defined values).
The concepts of defined-ness or truth-ness aren't defined for a sequence of scalars ("list") as a whole. They only apply to individual scalars. As such, //, &&, and, ||, or, !, not and ?: require a scalar from their left-most operand, so they evaluate it in scalar context. xor needs to test the truth of both of its operands, and thus evaluates both in scalar context.
$ perl -M5.010 -e'
sub cx { print wantarray ? " list " : " scalar"; $_[0] }
print "// "; #a = cx($u) // cx(); say "";
print "&& "; #a = cx(0) && cx(); say "";
print "and"; #a = ( cx(1) and cx() ); say "";
print "|| "; #a = cx(0) || cx(); say "";
print "or "; #a = ( cx(0) or cx() ); say "";
print "xor"; #a = ( cx(0) xor cx(0) ); say "";
print "! "; #a = ! cx(); say "";
print "not"; #a = not cx(); say "";
print "?: "; #a = cx() ? 0 : 0;
#a = 1 ? cx() : 0;
#a = 0 ? 0 : cx(); say "";
'
// scalar list
&& scalar
and scalar list
|| scalar list
or scalar list
xor scalar scalar
! scalar
not scalar
?: scalar list list
You might be under the incorrect impression that #a always returns a list of its elements, and that this gets coerced into a count when evaluated in scalar context. But that's not the case.
There's simply no such thing as a list. When we say "evaluates to a list" or "returns a list", we simply mean "adds zero or more scalar to the stack". There's absolutely no difference between return a scalar and returning "a list of one scalar". Both refer to adding a scalar to the stack.
Since there's no list data structure being returned, there's nothing that can be magically coerced to a scalar in scalar context; its up to every operator to return a single scalar when evaluated in scalar context. This permits each operator to choose what they want to return in scalar context. It varies greatly.
In short, #a returns the number of elements in scalar context, not the elements it contains like it does in list context.
+-------------------------- (Scalar context) Returns 3
| +------------- (List context) Returns three scalars
| |
vvvv vvvvvvvv
() = #row // (10,20,30);
+-------------------------- (Scalar context) Returns 3
| +------------------- (List context) Returns three scalars
| | +------- (List context) Returns three scalars
| | |
vvvv vvvv vvvvvvvv
() = #row ? #row : (10,20,30);
Finally, let's analyze #row // ....
We've already established that #row is evaluated in scalar context in the above, and that it returns the number of elements in the array in array when it does.
Well, numerical values are necessarily not undef, so they are all defined. So that means that the right-hand size of #row // ... will never get evaluated. You might as well have written scalar(#row).
defined(#array) is a bit of a weird construct and Perl is trying to get rid of it. From perldoc -f defined:
Use of "defined" on aggregates (hashes and arrays) is no longer supported. It used to report whether memory for that aggregate had ever been allocated.
That's not testing if it currently has any elements! To check for size, simply use the array variable in scalar context (such as a conditional):
if( #array ) { ... }
And, Dada has already explained what the defined-or operator does, which adds its own twist to it.
// and || first impose scalar context on their left side argument. // checks whether that argumend is defined. || checks whether that argument is "not false". false in Perl is any of the values undef, 0, "0", or "".
The scalar value of an array is always defined so you can't use that in a check the way you did. #row // (10,20,30) is the same as defined(scalar(#row)) or (10,20,30).
For a longer discussion of context, see the documentation for the Want module.
In perl all sub's argument writting to #_ array, like this:
call_any_sub($a,$b,$c);
sub call_any_sub {
my $s_a = shift;
my $s_b = shift;
my $s_c = shift;
}
But, if i want to passed array as an argument to sub, i should use:
call_any_sub(#data_array);
sub call_any_sub {
my #data = #_;
}
Instead of similar:
call_any_sub(#data_array);
sub call_any_sub {
my #data = shift;
}
So, why #data_array replaces the array of arguments and not written in it (as expected)?
One can only pass a list of scalars to a subroutine (and that's all they can return). After all, the arguments are presented to the sub as an array (#_), and arrays can only contains scalars.
You can either (inefficiently) recreate the array in the sub
sub foo {
my #bars = #_;
say for #bars;
}
foo(#bars);
or you can pass a reference to the array
sub foo {
my ($bars) = #_;
say for #$bars;
}
foo(\#bars);
You need to understand what shift does.
The shift/unshift pair of commands are parallel to the pop/push pair of commands. All of these commands operate on arrays. By default, shift (and only shift) assumes the #_ array when called in a subroutine and #ARGV when called in the main program. This means the following two statements are identical in a subroutine:
my $foo = shift #_; # Explicit Argument
my $foo = shift # Implicit Argument
Perl's parameter passing is an interesting concept because it doesn't really do named parameter passing like almost all other programs. Instead, everything is passed as one long list of scalars. This makes it hard when you aren't passing in a scalar.
It works okay if I am only passing in a single hash or array:
munge_hash ( %foo );
sub munge_hash {
my %hash = #_;
...
}
And, you have to be careful if you're passing in multiple arguments and an array. In this case, the array must be the last in your list of arguments:
my $foo = "floop";
my $bar = "bloop";
my #array = qw(loop coop soop);
munge_this ( $foo, $bar, #array );
sub munge_this {
say join ":", #_; # Prints "floop:bloop:loop:coop:soop"
my $var1 = shift # floop
my $var2 = shift # bloop
my #arry = #_ # The rest is the array passed.
However, things really fall apart if you're passing in multiple arrays or hashes. All of the elements get merged into a single list of scalars represented by #_.
munge_two_arrays ( #foo, #bar );
sub munge_two_arrays {
# Problem! Elements of both arrays are in #_.
# How do I separate them out?
}
Thus, it is common not to pass in a whole array, but an array reference:
munge_two_arrays( \#foo, \#bar ); # These are array references
sub munge_two_arrays {
my $array1_ref = shift;
my $array2_ref = shift;
my #array1 = #{ $array1_ref } # Dereference array references to make arrays
my #array2 = #{ $array2_ref } # Dereference array references to make arrays
}
This keeps the values of the two arrays from getting merged into a single #_.
The below one the code.
sub max
{
if (#_[0] > #_[1])
{
#_[0];
}
else
{
#_[1];
}
}
print "biggest is ".&max(37,25);
When I ran it, I got the following warnings,
Scalar values #_[0] better written as $_[0] at file.pl line 3.
Scalar values #_[1] better written as $_[1] at file.pl line 3.
Scalar values #_[0] better written as $_[0] at file.pl line 5.
Scalar values #_[0] better written as $_[0] at file.pl line 9.
biggest is 37.
Although I got a correct output, but I wonder what could be the reason behind this warning, Since I think that using #_ in a subroutine would be apropriate than $_.
The problem is that you are referring to your single array element by using an array slice instead of a scalar. Just like the error says. An array slice is a list of elements from an array, for example:
my #a = (0 .. 9);
print #a[0,3,4]; # prints 034
Conversely, when you refer to a single array element you use the scalar prefix $:
print $a[3]; # prints 3
So when you do
#_[0];
Perl is telling you that the proper way to refer to a scalar value is by not using an array slice, but rather to use the scalar notation:
$_[0];
That is all.
Try to understand it with this example:
#array = (1,2,3); #array is the name of the array and # means that it's an array
print $array[1];
#this will print element at index 1 and you're doing it in scalar context
Similarly,
#_ = (1,2,3); #_ is the name of the array
print $_[1];
#this will print element at index 1 of array _ and again you're doing it in scalar context
You are referring to an array, instead of a scalar. #_[0] means ($_[0]). But perl is kind of clever so it warns You that instead of an explicit single element list You should return a scalar. Here You should use $_[0].
I suggest to use prototype, as now You could call max (1, 2, 3) and the result will be 2, instead of 3. So define as
sub max ($$) { $_[0] > $_[1]) ? $_[0] : $_[1] }
Or better, You can use for undefined number (>=2) of elements. Maybe pointless to call it with 0 or 1 items.
sub max (#) {
return undef if $#_<0;
my $s = shift;
for(#_) { $s = $_ if $_ > $s } $s
}
I have a question regarding pass by reference to subroutines in Perl. For values if I pass using #_ it works but for ref only shift works . Not sure why. I have givedn sample code below:
This works:
#! /usr/bin/perl
use strict;
use warnings;
my $name = 'John';
PassScalarByRef( \$name );
sub PassScalarByRef{
my $got = shift;
print "Hello $$got\n";
}
but not this one:
#! /usr/bin/perl
use strict;
use warnings;
my $name = 'John';
PassScalarByRef( \$name );
sub PassScalarByRef{
my $got = #_;
print "Hello $$got\n";
}
In the second case, assigning to $got provides a scalar context to #_, which causes it to evaluate to its size (number of elements). You can instead say:
my ($got) = #_;
...to assign the first element of #_ to $got, as you expect.
You are using the #_ array in scalar context. $got now contains the number of arguments passed. You should try my ($got) = #_, which now uses the array in list context which is what you mean.
Most operators give their operands a specific context in a consistent way; for instance, + gives both its operands scalar context; || gives its left operand scalar context and its right operand whatever context the || itself has.
Assignment is a little different, because there are two types, list assignment and scalar assignment.
Scalar assignments look like:
$scalar = ...
lvaluesub() = ...
(lvalue subs are a little-used feature of perl; the builtin pos is an example).
Only one value is being assigned, and these give ='s right operand scalar context.
List assignments look like this:
#array = ...
#arraytoslice[...] = ...
%hash = ...
#hashtoslice{...} = ...
( ... ) = ...
or even
() = ...
All these expect a list of values to assign, so give the right operand list context.
When you say:
my $got = #_;
this is a scalar assignment, and so #_ gets scalar context, which causes it to return its number of elements, not the first value.
Instead, say:
my ($got) = #_;
Some people do this consistently, even for subs with only one operand; others do
my $param1 = shift;
my $param2 = shift;
for subs with a small number of operands.
It's common for methods to get the object/class using shift and a list assignment from #_ for the remaining parameters.
Used differently.
my $got = $_[0];
Consider:
sub abc()
{
}
abc(#array, $a);
How do I access #array and $a in subroutine abc()?
I know about $_[0] and $_[1], but I wasn't sure if I can use it for arrays.
You access a sub's arguments with the #_ array. The first argument is $_[0], the second - $_[1], etc. In this particular case, your array will be unrolled to list of its elements, so $_[0] is $array[0], $_[1] is $array[1] and then after all those elements, last element of #_ will be the value of $a.
If you want to avoid unrolling that always happens when you use an array in a list context, use a reference to the array instead. References to arrays and hashes are created using \. So call your function like:
abc(\#array, $a);
After that, $_[0] will have reference to #array and $_[1] will be $a. To access array elements through reference, use -> operator. $_[0]->[2] is same as $array[2]. Actually you can even drop -> as long as it is between brackets, so $_[0][2] will work too. See more details on references in perlref.
You have two options:
Pass the scalar variable first (the dirty way)
abc($a, #array);
Then receive the parameters in subroutine as
my ($a, #array) = #_;
Pass your array as reference by adding a backslash before the array variable (recommended)
abc(\#array, $a);
Then receive the parameters in subroutine as
my ($array_ref, $a) = #_;
And dereference the $array_ref
my #array = #$array_ref;
More information about perlref.
The other answers explained the two basic approaches. However, it is important to note that there is a big difference between the two: When you pass an array by reference, any changes you make to it also change the original array. Here is an example:
use warnings;
use strict;
my #array = (1, 2, 3, 4, 5);
sub by_ref
{
my $array_ref = $_[0];
#$array_ref = (0, 0, 0);
print "Array inside by_ref: #$array_ref\n";
}
sub by_val
{
my #array_copy = #_;
#array_copy = (0,0,0);
print "Array inside by_val: #array_copy\n";
}
by_val(#array);
print "Original array after calling by_val: #array\n";
by_ref(\#array);
print "Original array after calling by_ref: #array\n";
If you do pass by reference, you need to keep this behavior in mind, making a copy of the referenced array if you don't want changes made in your sub to affect the original.
It would be nice if you pass the array reference instead of an array as mentioned by Oleg V. Volkov like
sub abc()
{
my ( $array, $a ) = #_; #receiving the paramters
my #arr = #{$array}; # dereferencing the array
}
abc(\#array,$a);