Pass by reference in perl - perl

use warnings;
use strict;
my #array = (1,2,3,4,5);
my $v = 1;
sub by_ref
{
my ($array_ref,$v) = #_;
#$array_ref = (0,0,0);
print "Array inside by_ref: #$array_ref\n";
}
by_ref(\#array,$v);
print "Array changed: #$array\n";
I'm passing #array by reference(I'm assuming I'm doing it right). I want the changes made in the sub routine on #arraybe reflected in the calling sub routine. I don't know where I have gone wrong.
Thank you in advance.

You are printing the array reference outside the subroutine too, which is wrong. The scope of array reference is limited to the subroutine only.
So you should change your last line to print only #array not #$array.
Like:
print "Array changed: #array\n";

Just change to
print "Array changed: #array\n";
and it should be ok

Related

Perl: Passing by reference does not modify the hash

My understanding was that in Perl we pass hashes to functions by reference
Consider the following example, where we modify the hash in the modifyHash function
#!/usr/local/bin/perl
my %hash;
$hash{"A"} = "1";
$hash{"B"} = "2";
print (keys %hash);
print "\n";
modifyHash(\%hash);
print (keys %hash);
print "\n";
sub modifyHash {
my $hashRef = #_[0];
my %myHash = %$hashRef;
$myHash{"C"} = "3";
print (keys %myHash);
print "\n";
}
The output of this script is:
AB
ABC
AB
I would have expected it to be:
AB
ABC
ABC
...as we pass the hash by reference.
What concept am I missing here about passing hashes to functions?
That's because when you do my %myHash = %$hashRef;, you're taking a copy of the dereferenced $hashref and putting it into %myHash which is the same thing as my %myHash = %hash;, so you're not working on the referenced hash at all.
To work on the hash specified by the reference, try this...
sub modifyHash {
my $hashRef = $_[0];
$hashRef->{"C"} = "3";
print (keys %$hashRef);
print "\n";
}
As pointed out by ThisSuitIsBlackNot in the comments below, #_[0] is better written as $_[0]. You should always be using use strict; and use warnings;, as this would have been caught. Because you're sending in a reference, you could also have used my $hashRef = shift;.
The problem is with the assignment:
my %myHash = %$hashRef;
This is akin to saying:
$x = 5;
$y = $x;
You're not setting $y to reference the same spot in memory, you're just giving the value of $x to $y. In your example, you're creating a new hash (%myHash) and giving it the value of the hash stored at $hashRef. Any future changes are to the new hash, not the original.
If you want to manipulate the original, you should do something like:
${$hashRef}{"C"} = "3";
or
$hashRef->{"D"} = 4;
There might be a more elegant way of doing it, but as far as I know you want to work with the hash reference.

Not able to print value using reference inside function in perl

Why am i not being able to print the value using reference when its inside function?
sub fun {
$ref = #_;
print "\n Inside the function $ref->[1] \n";
}
my #arr=(2,3,4);
fun (\#arr);
my $ref2 = \#arr;
print "\n$ref2->[1]\n";
Output i get is :
Inside the function
3
It is your assignment that is wrong:
$ref = #_;
Because in scalar context, an array returns its size, not its elements. Scalar context is imposed when you have a scalar value on the left hand side. You should do:
my ($ref) = #_;
Or
my $ref = shift; # shifts first argument from #_
You also need to change $ref->[1] to $ref->[0], or you will refer to the wrong element. Perl arrays start at index 0.
What you should have done is to use
use strict;
use warnings;
Which would have given you the error:
Can't use string ("1") as an ARRAY ref while "strict refs" in use at line ...
Which is what happens. You assign the size 1 to $ref, and then try to use it as an array ref: $ref->[1]

Perl: Special array #_ is not really an alias?

The special array, #_ , where all the arguments passed to a function are present, is actually an alias to the arguments passed. Hence, any change we make directly to this special array #_ will reflect in the main as well. This is clear.
#!/usr/bin/perl
use warnings;
use strict;
$\="\n";
sub func {
print \#_;
$_++ for(#_);
}
my #arr=(2..4);
print \#arr;
func(#arr);
print "#arr";
For the above program, I expected the reference of #arr and #_ to point to the same location since it is an alias. But it is not so.
On running the above:
ARRAY(0x1b644d0)
ARRAY(0x1b644e0)
3 4 5
If they are pointing to 2 different locations, how the changes done in #_ is reflecting in #arr?
Am I seeing something wrong? Please advice.
This might answer you question:
use warnings;
use strict;
$\="\n";
sub func {
print \#_;
$_++ for(#_);
print \$_ for #_;
}
my #arr=(2..4);
print \#arr;
func(#arr);
print "#arr";
print \$_ for #arr;
Output
ARRAY(0x17fcba0)
ARRAY(0x1824288)
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)
3 4 5
SCALAR(0x17fcc48)
SCALAR(0x18196f8)
SCALAR(0x1819710)
As you see, individual arguments have the same address but the container is not the same. If you push an item to #_ in func the #arr will not change (so you can do shift in funct). So, each argument is an alias and array elements are passed as individual items. #_ contains all items passed into the subroutine. If you want to modify an array argument you need to pass it by reference.
#_ isn't aliased; its elements are.
Remember that
func(#arr);
is the same as
func($arr[0], $arr[1], ...);
because the only thing that can be passed to a sub is a list of scalars, and an array evaluates to a list of its elements in list context.
So that means
func(#arr);
is basically the same as
local #_;
alias $_[0] = $arr[0];
alias $_[1] = $arr[1];
...
&func;
Changing the elements of #_ will change elements of #arr, but adding and removing elements of #_ won't change #arr since they are different arrays.
>perl -E"#a=(4..6); sub { $_[0] = '!'; say #_; }->(#a); say #a;"
!56
!56
>perl -E"#a=(4..6); sub { splice(#_,0,1,'!'); say #_; }->(#a); say #a;"
!56
456

Why does my Perl max() function always return the first element of the array?

I am relatively new to Perl and I do not want to use the List::Util max function to find the maximum value of a given array.
When I test the code below, it just returns the first value of the array, not the maximum.
sub max
{
my #array = shift;
my $cur = $array[0];
foreach $i (#array)
{
if($i > $cur)
{
$cur = $i;
}
else
{
$cur = $cur;
}
}
return $cur;
}
Replace
my #array = shift;
with
my #array = #_;
#_ is the array containing all function arguments. shift only grabs the first function argument and removes it from #_. Change that code and it should work correctly!
Why don't you want to use something that works?
One of the ways to solve problems like this is to debug your data structures. At each step you print the data you have to see if what you expect is actually in there. That can be as simple as:
print "array is [#array]\n";
Or for complex data structures:
use Data::Dumper;
print Dumper( \#array );
In this case, you would have seen that #array has only one element, so there it must be the maximum.
If you want to see how list assignment and subroutine arguments work, check out Learning Perl.
You can write the function as:
#!/usr/bin/perl
use strict; use warnings;
print max(#ARGV);
sub max {
my $max = shift;
$max >= $_ or $max = $_ for #_;
return $max;
}
However, it would be far more efficient to pass it a reference to the array and even more efficient to use List::Util::max.

Why does map return an empty array?

I have a problem in Perl I don't understand. I stripped it down to this very short code.
Why does Perl's map function return an empty array? Shouldn't it return an array with 9 undefs?
sub mySub{
return;
}
my #arr = (1 .. 9);
my #arr2 = map( mySub($_), #arr );
print #arr . ' ' . #arr2, "\n";
It prints "9 0".
It is probably something simple, but perldoc is not helping.
The more general answer to your question is this: when return is used without an argument, the value it returns depends on the calling context:
list context returns an empty list
scalar context returns an undefined value
For example:
use strict;
use warnings;
use Data::Dumper;
my (#list);
sub mySub { return }
#list = map( mySub($_), 1..2); print Dumper(\#list);
#list = map(scalar mySub($_), 1..2); print Dumper(\#list);
Output:
$VAR1 = [];
$VAR1 = [
undef,
undef
];
You subroutine is not returning undef, it is returning an empty list. 9 times and empty list is still an empty list.
Try explicitly returning undef and the output will be different.
Try this
use strict;
use warnings;
sub mySub{
return undef;
}
my #arr = (1,2,3,4,5,6,7,8,9);
my #arr2 = map(&mySub, #arr);
print #arr." ".#arr2;
If you need to get list containing undefs, you need to return undef explicitly. The thing is that map calls your mySub in array context (check what wantarray gives you from this sub). return statement essentially gives back an empty list each time your sub is called, which results in empty array in total