run a subroutine by using argument - perl

My code:
my $aaa = "abc";
sub myp{
print "$_";
}
myp($aaa);
I hope myp can print the argument it get.
But it said
Use of uninitialized value $_ in string at ./arg line 17.

The arguments to a subroutine in Perl are passed in the #_ array. This is not the same as the $_ variable.
A common idiom is to "unpack" these arguments in the first line of a function, e.g.
sub example {
my ($arg1, $arg2) = #_;
print "$arg1 and $arg2";
}
It's also possible to refer to arguments directly as elements of #_, e.g. as $_[0], but this is much harder to read and as such is best avoided.

I usually do something like:
my $first_arg = shift #_;
my $second_arg = shift #_;
You can also use the method of the other response:
my ($first_arg, $second_arg) = #_;
But be careful saying:
my $first_arg = #_;
Since you will get the number of arguments passed to the subroutine.
When you refer to $_ you are referencing the default string variable, you probably want in this case to refer #_, if you want to get a specific argument, you must say $_[narg], be also careful passing arrays to subroutines if you do:
some_sub(#myarray);
You will pass the entire array as it was the argument list, instead you should say:
some_sub(\#myarray);

Related

Perl: passing hash by ref using rule1

I am still unclear about why by ref portion is showing undefined value for %Q and $_ uninitialized. I have been looking through perlreftut and still unable to see what I have done wrong. Passing the hash as a flat array has no issue.
Doing it by ref with testRef(\%mkPara) passes a scalar hash reference to the subroutine, right? So, does my %Q = %{$_} not turn it back into a hash?
use strict;
use diagnostics;
use warnings;
my %mkPara = ('aa'=>2,'bb'=>3,'cc'=>4,'dd'=>5);
sub testFlat
{
my %P = #_;
print "$P{'aa'}, $P{'bb'}, ", $P{'cc'}*$P{'dd'}, "\n";
}
sub testRef
{
my %Q = %{$_}; #can't use an undefined value as HASH reference
#print $_->{'aa'}, "\n";#Use of uninitialized value
print $Q{'aa'},"\n";
}
#testFlat(%mkPara);
testRef(\%mkPara);
When you use arguments in a function call (\%mkPara in your case), you can access them through #_ array inside the function.
Here, you pass a single argument to the function : \%mkPara, which you can then access by accessing the first element of #_ by using $_[0].
$_ is the default variable for some builtin functions/operators (print, m//, s///, chomp and a lot more). Usually seen in while or for loops. But in your code, you have no reason to use it (you are never setting it to anything, so it's still set to undef, hence the error "Can't use an undefined value as a HASH reference".
So your function should actually be :
sub testRef
{
my %Q = %{$_[0]}; # instead of %{$_}
print $_[0]->{'aa'}, "\n"; # instead of $_->{'aa'}
print $Q{'aa'},"\n";
}
If needed, you can find more about functions on perlsub.
However, as #Ikegami pointed out in the comments, using my %Q = %{$_[0]}; creates a copy of the hash you sent to the function, which in most cases (including that one where you just print a key of the hash) is very suboptimal as you could just use a hashref (like you are doing when you do $_[0]->{'aa'}).
You can use hash references like this (roughly the same example as the answer of #Zaid) :
sub testRef
{
my ( $Q ) = #_;
print $Q->{aa} ;
print $_, "\n" for keys %$Q;
}
testRef(\%mkPara);
There are quite a lot of resources about references online, for instance perlreftut that you were already looking at.
This can seem a bit tricky at first, but the reason is that $_ is not the same as #_.
From perlvar:
$_ is the implicit/"default" variable that does not have to be spelled out explicitly for certain functions (e.g. split )
Within a subroutine the array #_ contains the parameters passed to that subroutine
So the reason why
my %Q = %{$_};
says you can't use an undefined value as hash reference is because $_ is not defined.
What you really need here is
my %Q = %{$_[0]};
because that is the first element of #_, which is what was passed to testRef in the first place.
In practice I tend to find myself doing things a little differently because it lends itself to flexibility for future modifications:
sub testRef {
my ( $Q ) = #_;
print $_, "\n" for keys %$Q; # just as an example
}

If perl is call-by-reference why does this happen?

I've read that perl uses call-by-reference when executing subrutines. I made a simple piece of code to check this property, but it behaves like if perl was call-by-value:
$x=50;
$y=70;
sub interchange {
($x1, $y1) = #_;
$z1 = $x1;
$x1 = $y1;
$y1 = $z1;
print "x1:$x1 y1:$y1\n";
}
&interchange ($x, $y);
print "x:$x y:$y\n";
This produces the following output:
$ perl example.pl
x1:70 y1:50
x:50 y:70
If arguments were treated in a call-by-reference way, shouldn't x be equal to x1 and y equal to y1?
Perl is always definitely call by reference. You're statement ($x1, $y1) = #_ is copying the original argument values, since #_ holds aliases to the original parameters.
From perlsub manpage:
Any arguments passed in show up in the array #_ . Therefore, if you called a function with two arguments, those would be stored in $[0] and $[1] . The array #_ is a local array, but its elements are aliases for the actual scalar parameters. In particular, if an element $_[0] is updated, the corresponding argument is updated (or an error occurs if it is not updatable).
To modify the values outside of the sub, you would have to modify the values of #_.
The following sub interchange does modify the values:
sub interchange {
($x1, $y1) = #_; # this line copies the values to 2 new variables
$z1 = $x1;
$x1 = $y1;
$y1 = $z1;
$_[0] = $x1; # this line added to change value outside sub
$_[1] = $y1; # this line added to change value outside sub
print "x1:$x1 y1:$y1\n";
}
This gives the output:
x1:70 y1:50
x:70 y:50
More info here: http://www.cs.cf.ac.uk/Dave/PERL/node51.html
But, to quote the article:
You can see that the function was able to affect the #array variable in the main program. Generally, this is considered bad programming practice because it does not isolate what the function does from the rest of the program.
I'm just starting with Perl as well, and I believe you're misunderstanding just what you're passing to the subroutine. When you pass $x and $y you are passing the scalars $x and $y are set to. You need to explicitly pass a reference, which also happens to be a scalar (being the only thing are ever allowed to pass to subroutines). I understand where you're coming from in thinking things are call-by-reference since for arrays and hashes, since you need to pass references to those.
This code should do what you're looking for:
#!/usr/bin/perl
$x=50;
$y=70;
sub interchange {
($x1, $y1) = #_;
$z1 = $$x1; # Dereferencing $x1
$$x1 = $$y1; # Dereferencing $x1 and $y1
$$y1 = $z1; # Dereferencing $y1
print "x1:$$x1 y1:$$y1\n";
}
&interchange (\$x, \$y); # Passing references to $x and $y, not their values
print "x:$x y:$y\n";
I pass in references to $x and $y using \$x and \$y. Then, I use $$x and $$y to dereference them within the subroutine.

Perl map passing arguments

I'm trying map() with my own subroutine. When I tried it with a Perl's builtin function, it works. But when I tried map() with my own subroutine, it fails.
I couldn't point out what makes the error.
Here is the code snippet.
#!/usr/bin/perl
use strict;
sub mysqr {
my ($input) = #_;
my $answer = $input * $input;
return $answer;
}
my #questions = (1,2,3,4,5);
my #answers;
#answers = map(mysqr, #questions); # doesn't work.
#answers = map {mysqr($_)} #questions; #works.
print "map = ";
print join(", ", #answers);
print "\n";
Map always assigns an element of the argument list to $_, then evaluates the expression. So map mysqr($_), 1,2,3,4,5 calls mysqr on each of the elements 1,2,3,4,5, because $_ is set to each of 1,2,3,4,5 in turn.
The reason you can often omit the $_ when calling a built-in function is that many Perl built-in functions, if not given an argument, will operate on $_ by default. For example, the lc function does this. Your mysqr function doesn't do this, but if you changed it to do this, the first form would work:
sub mysqr {
my $input;
if (#_) { ($input) = #_ }
else { $input = $_ } # No argument was given, so default to $_
my $answer = $input * $input;
return $answer;
}
map(mysqr, 1,2,3,4,5); # works now
The difference is that in the second case, you are explicitly passing the argument, and in the first one, you pass nothing.
#answers = map(mysqr, #questions); # same as mysqr(), no argument passed
#answers = map {mysqr($_)} #questions; # $_ is passed on to $input
You might be thinking of the fact that many Perl built-in functions use $_ when no argument is given. This is, however, not the default behaviour of user defined subroutines. If you want that functionality, you need to add it yourself. Though be warned that it often is not a good idea.
Note that if you use use warnings, which you always should, you will get a descriptive error:
Use of uninitialized value $input in multiplication (*) at foo.pl line 8.
Which tells you that no data is passed to $input.
Not using warnings is not removing errors from your code, it is merely hiding them, much like hiding the "low oil" warning lamp in a car does not prevent engine failure.

perl subroutine argument lists - "pass by alias"?

I just looked in disbelief at this sequence:
my $line;
$rc = getline($line); # read next line and store in $line
I had understood all along that Perl arguments were passed by value, so whenever I've needed to pass in a large structure, or pass in a variable to be updated, I've passed a ref.
Reading the fine print in perldoc, however, I've learned that #_ is composed of aliases to the variables mentioned in the argument list. After reading the next bit of data, getline() returns it with $_[0] = $data;, which stores $data directly into $line.
I do like this - it's like passing by reference in C++. However, I haven't found a way to assign a more meaningful name to $_[0]. Is there any?
You can, its not very pretty:
use strict;
use warnings;
sub inc {
# manipulate the local symbol table
# to refer to the alias by $name
our $name; local *name = \$_[0];
# $name is an alias to first argument
$name++;
}
my $x = 1;
inc($x);
print $x; # 2
The easiest way is probably just to use a loop, since loops alias their arguments to a name; i.e.
sub my_sub {
for my $arg ( $_[0] ) {
code here sees $arg as an alias for $_[0]
}
}
A version of #Steve's code that allows for multiple distinct arguments:
sub my_sub {
SUB:
for my $thisarg ( $_[0] ) {
for my $thatarg ($_[1]) {
code here sees $thisarg and $thatarg as aliases
last SUB;
}
}
}
Of course this brings multilevel nestings and its own code readability issues, so use it only when absolutely neccessary.

Why pass by ref needs shift instead of #_?

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];