Perl - Changing a parent reference that is passed into a sub - perl

I am new to perl and couldn't find any info on my problem, so apologies in advance if this is a duplicate or I am completely out to lunch.
Actual question a little lower.
#!/usr/bin/perl
use strict;
my #root = undef;
myfunc(\#root);
sub myfunc()
{
my ($ptr_parent) = #_;
print "root => ";
print \#root;
print "\n";
print "ptr_parent => $ptr_parent\n";
my #stuff = split(',', 'test1,test2,test3');
$ptr_parent = \#stuff;
print "stuff => ";
print \#stuff;
print "\n";
print "root => ";
print \#root;
print "\n";
print "ptr_parent => $ptr_parent\n";
}
Not surprisingly my output is as follows:
root => ARRAY(0x7f903182e890)
ptr_parent => ARRAY(0x7f903182e890)
stuff => ARRAY(0x7f9031838548)
root => ARRAY(0x7f903182e890)
ptr_parent => ARRAY(0x7f9031838548)
What I am trying to achieve is the following:
root => ARRAY(0x7f903182e890)
ptr_parent => ARRAY(0x7f903182e890)
stuff => ARRAY(0x7f9031838548)
root => ARRAY(0x7f9031838548) <---- Note this changes.
ptr_parent => ARRAY(0x7f9031838548)
So I have a suspicion that this is impossible to do like this because of scoping as I would imagine that the #stuff memory pointer gets released.
I feel like I could make #root a $root where it is a pointer to a pointer. But I am stuck on how to write this. The syntax for pointers is still not 100% in my head.
But the general requirement is that whatever list is generated in myfunc would need to survive in #root. And I cannot modify #root directly from myfunc();
Thanks in advance. Let me know if I need to clarify.

In your code, $ptr_parent is a variable that holds a reference, and you are modifying the variable, not the referent. You need to use some dereference syntax if you want to modify the actual thing the reference points to. See perlreftut for an introduction and perlref for all the gory details.
use strict;
use warnings;
use Data::Dump;
my #arr;
dd(\#arr);
foo(\#arr);
dd(\#arr);
bar(\#arr);
dd(\#arr);
sub foo {
my $aref = shift;
#$aref = split(/,/, 'test1,test2,test3'); # notice the dereference
}
sub bar {
my $aref = shift;
$aref->[0] = 42; # individual element dereference
$aref->[1] = 'ponies';
}
Output:
[]
["test1", "test2", "test3"]
[42, "ponies", "test3"]

Perl passes by reference, which means that changing the argument in the sub will change it in the caller too. But you're not modifying the argument ($_[0]); you're modifying a lexical variable ($ptr_parent).
All of the following will work with myfunc($root):
# Create the object as needed, and pass it back explicitly on exit.
sub myfunc {
my $parent = $_[0] // [];
push #$parent, split(/,/, 'test1,test2,test3');
$_[0] = $parent;
}
# Create the object, and pass it back explicitly up front.
sub myfunc {
my $parent = shift //= [];
push #$parent, split(/,/, 'test1,test2,test3');
}
# Create the object as needed by using a layer of indirection.
sub myfunc {
my $parent_ref = \shift;
push #{ $$parent_ref }, split(/,/, 'test1,test2,test3');
}
Or you could be less "magical" and pass a reference to myfunc (myfunc(\$root)):
sub myfunc {
my $parent_ref = shift;
push #{ $$parent_ref }, split(/,/, 'test1,test2,test3');
}
By the way, the prototype was incorrect. I fixed this issue by removing it.

Indeed, you need to use a reference, and pass a reference to that reference if you want to modify it from a different place.
#!/usr/bin/perl
use strict;
my $root = [];
myfunc(\$root);
sub myfunc()
{
my ($ptr_parent) = #_;
print "root => ";
print $root;
print "\n";
print "ptr_parent => $$ptr_parent\n";
my #stuff = split(',', 'test1,test2,test3');
$$ptr_parent = \#stuff;
print "stuff => ";
print \#stuff;
print "\n";
print "root => ";
print $root;
print "\n";
print "ptr_parent => $$ptr_parent\n";
}
Output:
root => ARRAY(0x7f8143001ee8)
ptr_parent => ARRAY(0x7f8143001ee8)
stuff => ARRAY(0x7f8143022ad8)
root => ARRAY(0x7f8143022ad8)
ptr_parent => ARRAY(0x7f8143022ad8)

Related

Deep cloning of inside-out Perl classes - how to use methods from copied objects?

I have 3 classes declared as inside-out Perl classes using Class::Std. In one of these 3, there's a hash reference stored in $basket{ident $self} that looks like so (output of Data::Dumper):
$VAR1 = {
'auto' => {
'items' => {
'abc' => bless( do{\(my $o = undef)}, 'Item' )
},
'obj' => bless( do{\(my $o = undef)}, 'Category' )
}
};
I need to take this hash reference and create everything in it again (deep cloning). I tried to use dclone from Storable like so:
my $new_basket = dclone $basket{ident $self};
When I print the hashes, I get different memory addresses:
print $new_basket, "\n";
print $basket{ident $self}, "\n";
print $new_basket->{auto}->{items}, "\n";
print $basket{ident $self}{auto}->{items}, "\n";
print $new_basket->{auto}->{items}->{abc}, "\n";
print $basket{ident $self}{auto}->{items}->{abc}, "\n";
this will output:
HASH(0x55d325306a20)
HASH(0x55d325245298)
HASH(0x55d323b35ca8)
HASH(0x55d3243dd968)
Item=SCALAR(0x55d323b45190)
Item=SCALAR(0x55d325306588)
When I don't use dclone and use my $new_basket = $basket{ident $self} instead, I get the same memory addresses. When I use my $new_basket = { %{ $basket{ident $self} } }, I get different addresses only on the first level, which should be a shallow copy. All this seems fine and expected.
So, to me it seems that dclone actually deep-copied everything because the addresses are different. But when I try to use a method inside Item like so:
print $new_basket->{auto}->{items}->{abc}->get_added_on();
print $basket{ident $self}{auto}->{items}->{abc}->get_added_on();
I get:
Use of uninitialized value in print at lib/Basket.pm line 231.
2020-05-30
clearly that dclone works differently than I naively thought.
How should I deep-copy this whole structure? I'd appreciate some help or reference to some article/doc where I can read what's going on here.
One solution is to create the whole structure again using constructors, but I thought I'd save some space and use dclone. That obviously didn't turn out very well.
EDIT: I've been asked to provide a minimal runnable demonstration, here it is:
#!/usr/bin/env perl
use strict;
use warnings;
{
package A;
use Class::Std;
use Data::Dumper;
use Storable qw(dclone);
my %basket :ATTR;
sub BUILD {
my ($self, $ident, $args_ref) = #_;
$basket{$ident}->{auto} = {};
my $c = C->new({ date => q{2020-05-30} });
$basket{$ident}->{auto}->{items}->{abc} = $c;
return;
}
sub deep_clone {
my $self = shift;
print Dumper $basket{ident $self};
# the next line prints "2020-05-30" as expected
print $basket{ident $self}->{auto}->{items}->{abc}->get_added_on();
my $new_basket = dclone $basket{ident $self};
# "Use of uninitialized value in print at ./deep-clone.pl line 35."
print $new_basket->{auto}->{items}->{abc}->get_added_on();
}
}
{
package C;
use Class::Std;
my %added_on :ATTR( :get<added_on> );
sub BUILD {
my ($self, $ident, $args_ref) = #_;
$added_on{$ident} = $args_ref->{date};
return;
}
}
####
my $a = A->new();
$a->deep_clone();
The newly created "C" object was never added to %added_on.
Your classes will have to provide custom handlers for Storable to handle them.
Added to "A":
sub STORABLE_freeze {
my ($self, $cloning) = #_;
my $ident = ident($self);
return "", {
basket => $basket{$ident},
# Other attributes...
};
}
sub STORABLE_thaw {
my ($self, $cloning, $serialized, $inner) = #_;
my $ident = ident($self);
$basket{$ident} = $inner->{basket};
# Other attributes...
}
Added to "C":
sub STORABLE_freeze {
my ($self, $cloning) = #_;
my $ident = ident($self);
return "", {
added_on => $added_on{$ident},
# Other attributes...
};
}
sub STORABLE_thaw {
my ($self, $cloning, $serialized, $inner) = #_;
my $ident = ident($self);
$added_on{$ident} = $inner->{added_on};
# Other attributes...
}
Then you can use freeze/thaw/dclone without problem.
sub deep_clone {
my $self = shift;
#print Dumper $basket{ident $self};
CORE::say $basket{ ident $self }{auto}{items}{abc}->get_added_on();
my $clone = dclone($self);
#print Dumper $basket{ident $self};
CORE::say $basket{ ident $clone }{auto}{items}{abc}->get_added_on();
}

How do I call a sub returned by reference by a Perl closure?

I'm trying to make subroutine closure working like an object.
However, I cannot call the returned subs references properly.
I receive Not a CODE reference at .\closure.pl line 22. error.
#!/usr/bin/perl
use strict;
use warnings;
sub number {
my ($value) = #_;
my $val = sub { $value };
my $inc = sub { ++$value };
my $dec = sub { --$value };
my %api = (
'val' => \$val,
'inc' => \$inc,
'dec' => \$dec,
);
return %api;
}
my %numb = number(42);
$numb{'inc'}->();
print $numb{'val'}->();
How to fix the code?
Code fixed
Yes, of course, an anonymous definition must return a reference. it means that it can be put directly in the %api. Perl doesn't complain and works like a charm :)
#!/usr/bin/perl
use strict;
use warnings;
sub number {
my ($value) = #_;
my %api = (
'val' => sub { $value },
'inc' => sub { ++$value },
'dec' => sub { --$value },
);
return \%api;
}
my $m = number(14);
my $n = number(41);
$m->{'dec'}->();
$n->{'inc'}->();
print $m->{'val'}->() . "\n"; # -> 13
print $n->{'val'}->() . "\n"; # -> 42
As discussed in perlref, the sub keyword without a name creates an anonymous subroutine and returns a reference to it. So you don't need to create another level of reference using the backslash; just pass the reference you already have as the value in the hash.

Reference found where even-sized list expected in Perl - Possible pass-by-reference error?

I have a Perl class/module that I created to display Bible verses. In it there is a hash that stores several verses, with the key being the book/chapter/verse and the value being the text. This hash is returned from the module.
I'm including the Bible class in a controller class, and that connection seems to work. The problem is I keep getting errors on executing. My IDE because I'm following a Lynda tutorial, is Eclipse with the EPIC plugin.
The error is:
Reference found where even-sized list expected at C:/Documents and Settings/nunya/eric.hepperle_codebase/lynda/lamp/perl5/Exercise Files/14 Modules/eh_bibleInspiration_controller.pl line 42.
Use of uninitialized value $value in concatenation (.) or string at C:/Documents and Settings/nunya/eric.hepperle_codebase/lynda/lamp/perl5/Exercise Files/14 Modules/eh_bibleInspiration_controller.pl line 45.
HASH(0x19ad454) =>
Here is the CONTROLLER class:
#!/usr/bin/perl
# eh_bibleInspiration_controller.pl by Eric Hepperle - 06/23/13
#
use strict;
use warnings;
use Data::Dumper;
use EHW_BibleInspiration;
main(#ARGV);
sub main
{
my $o = EHW_BibleInspiration->new; # instantiate new object.
my %bo_ref = $o->getBibleObj();
print "\$o is type: " . ref($o) . ".\n";
print "\%bo_ref is type: " . ref(\%bo_ref) . ".\n";
# exit;
$o->getVerseObj();
listHash(\%bo_ref);
message("Done.");
}
sub message
{
my $m = shift or return;
print("$m\n");
}
sub error
{
my $e = shift || 'unkown error';
print("$0: $e\n");
exit 0;
}
sub listHash
{
my %hash = #_;
foreach my $key (sort keys %hash) {
my $value = $hash{$key};
message("$key => $value\n");
}
}
Here is the class that returns the verses and has the method to pick a random verse:
# EHW_BibleInspiration.pm
# EHW_BibleInspiration.
#
package EHW_BibleInspiration;
use strict;
use warnings;
use IO::File;
use Data::Dumper;
our $VERSION = "0.1";
sub new
{
my $class = shift;
my $self = {};
bless($self, $class); # turns hash into object
return $self;
}
sub getVerseObj
{
my ($self) = #_;
print "My Bible Verse:\n";
my $verses = $self->getBibleObj();
# get random verse
#$knockknocks{(keys %knockknocks)[rand keys %knockknocks]};
# sub mysub {
# my $params = shift;
# my %paramhash = %$params;
# }
# my %verses = %{$verses};
# my $random_value = %verses{(keys %verses)[rand keys %verses]};
# print Dumper(%{$random_value});
}
sub getBibleObj
{
my ($self) = #_;
# create bible verse object (ESV)
my $bibleObj_ref = {
'john 3:16' => 'For God so loved the world,that he gave his only Son, that whoever believes in him should not perish but have eternal life.',
'matt 10:8' => 'Heal the sick, raise the dead, cleanse lepers, cast out demons. You received without paying; give without pay.',
'Luke 6:38' => 'Give, and it will be given to you. Good measure, pressed down, shaken together, running over, will be put into your lap. For with the measure you use it will be measured back to you.',
'John 16:24' => 'Until now you have asked nothing in my name. Ask, and you will receive, that your joy may be full.',
'Psalms 32:7' => 'You are a hiding place for me; you preserve me from trouble; you surround me with shouts of deliverance. Selah',
'Proverbs 3:5-6' => 'Trust in the LORD with all your heart, and do not lean on your own understanding. 6 In all your ways acknowledge him, and he will make straight your paths.',
'John 14:1' => 'Let not your hearts be troubled. Believe in God; believe also in me.'
};
my $out = "The BIBLE is awesome!\n";
return $bibleObj_ref;
}
1;
What am I doing wrong? I suspect it has something to do with hash vs hash reference, but I don't know how to fix it. My dereferencing attempts had failed miserably because I don't really know what I'm doing. I modeled my random getter off of something I saw on perlmonks. #$knockknocks{(keys %knockknocks)[rand keys %knockknocks]};
In the main, you have:
my %bo_ref = $o->getBibleObj();
but, in package EHW_BibleInspiration;, the method getBibleObj returns : return $bibleObj_ref;
You'd do, in the main : my $bo_ref = $o->getBibleObj();
and then call listHash($bo_ref);
Finaly, don't forget to change sub listHash to:
sub listHash
{
my ($hash) = #_;
foreach my $key (sort keys %{$hash}) {
my $value = $hash->{$key};
message("$key => $value\n");
}
}
In your main, you do
listHash(\%bo_ref);
This passes a hash reference to the sub. However, it tries to unpack its arguments like
my %hash = #_;
Oops, it wants a hash.
Either, we pass it a hash: listHash(%bo_ref). This saves us much typing.
Or, we handle the reference inside the sub, like
sub listHash {
my ($hashref) = #_;
foreach my $key (sort keys %$hashref) {
my $value = $hashref->{$key};
print "$key => $value\n";
}
}
Notice how the reference uses the dereference arrow -> to access hash entries, and how it is dereferenced to a hash for keys.
I suspect that your suspicion is correct. In your method sub listHash you're correctly passing in the hash but you're trying to use a hash instead of a hash reference for the internal variable. Try using my ($hash) = #_; instead of my %hash = #_;.
When using references you can use the -> operator to de-reference it to get to the underlying values. The rest of your method should look like this:
sub listHash
{
my ($hash) = #_;
foreach my $key (sort keys %{$hash}) {
my $value = $hash->{$key};
message("$key => $value\n");
}
}
On line 43 of your program I had to tell Perl that the reference should be a hash reference by calling keys %{$hash}. Then on line 44 I de-referenced the hash to get the correct value by calling $hash->{$key}. For more information on Perl and references you can read the through the tutorial.
listHash is expecting a hash and you're passing it a hash reference. Change:
listHash(\%bo_ref);
to:
listHash(%bo_ref);

Unable to pass a hash and a string to a function, together in perl!

I am basically trying to pass a string and a hash to a subroutine in perl.
sub coru_excel {
my(%pushed_hash, $filename) = #_;
print Dumper(%pushed_hash);
}
But it seems data is getting mixed up. The dumped data also includes the $filename. here is the output.
...................
$VAR7 = 'Address';
$VAR8 = [
'223 VIA DE
................
];
$VAR9 = 'data__a.xls' <----- $filename
$VAR10 = undef;
$VAR11 = 'DBA';
$VAR12 = [
'J & L iNC
..................
];
Here is how I called the subroutine.
coru_excel(%hash, "data_".$first."_".$last.".xls");
Arguments are passed to subroutines as one undifferentiated list.
One solution is to reverse the order of the arguments so that the scalar is first.
sub coru_excel {
my($filename, %pushed_hash) = #_;
}
coru_excel("FILE_NAME", %hash);
Another approach is to pass the hash by reference:
sub coru_excel {
my($pushed_hash_ref, $filename) = #_;
}
coru_excel(\%hash, "FILE_NAME");
You could pass the hash as a reference:
sub coru_excel {
my($pushed_hashref, $filename) = #_;
print Dumper(%$pushed_hashref);
}
coru_excel(\%my_hash, $file);
Or you could give special treatment to the final argument before you initialize the hash:
sub coru_excel {
my $filename = pop #_;
my(%pushed_hash) = #_;
print Dumper(%pushed_hash);
}
You have to pass the hash as a reference:
coru_excel(\%hash, "data_".$first."_".$last.".xls");
You use it like this:
sub coru_excel {
my($pushed_hash_ref, $filename) = #_;
my %pushed_hash = %{$pushed_hash_ref};
print Dumper(%pushed_hash); # better: \%pushed_hash or $pushed_hash_ref
}
See perlreftut for a tutorial on references and perlref for further information.
Dumper also produces better usable information when you pass a hash (or array) reference.
See also the related Perl FAQ. From the command line:
perldoc -q pass
or
perldoc -q hash
Refer to perlfaq7: How can I pass/return a {Function, FileHandle, Array, Hash, Method, Regex}?
A small program demonstrating how to do this using reference notation when passing the hash and shift in the subroutine to pull out the parameters.
#!/usr/bin/perl -w
use strict;
sub coru_excel(%$);
my %main_hash = ('key1' => 'val1', 'key2' => 'val2');
my $first = "ABC";
my $last = "xyz";
coru_excel(\%main_hash, "data_" . $first . "_" . $last . ".xls");
exit;
sub coru_excel(%$)
{
my %passed_hash = %{(shift)};
my $passed_string = shift;
print "%passed_hash:\n";
for my $k (keys %passed_hash) {
print " $k => $passed_hash{$k}\n";
}
print "\$passed_string = $passed_string\n";
return;
}

How can I loop through a list of functions in Perl?

I have a list of functions in Perl. Example:
my #funcs = qw (a b c)
Now they all belong to this module Foo::Bar::Stix. I would like to call them iteratively in a loop:
foreach $func (#funcs) {
Foo::Bar::Stix::$func->(%args)
}
where args is a hash of arguments. However I keep getting this error: "Bad name after :: ..." at the line which contains Foo::Bar::Stix::$func->(%args) How do I fix this error?
a b and c are not function objects but strings
Rather than storing the names of the functions in your array, store references to them in a hash so that you can refer to them by name. Here's a simple code example:
#!/usr/bin/perl
use strict;
use warnings;
my %func_refs = (
'a' => \&Foo::Bar::Stix::a,
'b' => \&Foo::Bar::Stix::b,
'c' => \&Foo::Bar::Stix::c
);
foreach my $func_ref ( values %func_refs ) {
print $func_ref->( "woohoo: " ), "\n";
}
{
package Foo::Bar::Stix;
sub a {
my $arg = shift;
return $arg . "a";
}
sub b {
my $arg = shift;
return $arg . "b";
}
sub c {
my $arg = shift;
return $arg . "c";
}
}
If you're stuck with storing the names for some reason, try this:
my $package = "Foo::Bar::Stix";
my #func_names = qw/ a b c /;
foreach my $func_name (#func_names) {
my $str = &{ "$package\::$func_name" }( "woohoo: " );
print $str, "\n";
}
However, this doesn't work under use strict, and because of this I prefer the first solution. Whatever you do, try to avoid using eval. It's unnecessary, and will likely only cause you problems.
Also, most people who work with Perl capitalize it as Perl rather than PERL. Here's a Stackoverflow question on the subject:
How should I capitalize Perl?
Bad answer: use a symbolic reference:
for $func (#funcs) {
&{"Foo::Bar::Stix::$func"}(\%args);
}
Good answer: use a dispatch table:
my %call_func = (
'a' => \&Foo::Bar::Stix::a,
'b' => \&Foo::Bar::Stix::b,
'c' => \&Foo::Bar::Stix::c,
);
...
for $func (#funcs) {
$call_func{$func}->(\%args);
}
Slight change of syntax will give you what you want
Foo::Bar::Stix->$func(%args)
Though this will pass the package name as the first parameter.
You can use can
my #funcs = qw (a b c)
foreach $func (#funcs) {
Foo::Bar::Stix->can($func)->(%args)
}
You could access it through the special %Foo::Bar::Stix:: variable. This gives full access directly to the symbol table. You'll also notice that it works under strict mode.
#! /usr/bin/env perl
use strict;
use warnings;
{
package Foo::Bar::Stix;
sub a{ print "sub a\n" }
sub b{ print "sub b\n" }
sub c{ print "sub c\n" }
}
my #funcs = qw' a b c ';
my %args;
for my $func (#funcs) {
$Foo::Bar::Stix::{$func}->(%args); # <====
}
Another option:
my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};
my %funcs = (
# we only want the CODE references
'a' => *{ $symbol_table->{'a'} }{'CODE'},
'b' => *{ $symbol_table->{'b'} }{'CODE'},
'c' => *{ $symbol_table->{'c'} }{'CODE'},
);
for my $func (#funcs) {
$funcs{$func}->(%args); # <====
}
If you are going to be doing that for a large number of subroutines, this is how I would load up the %funcs variable.
my %funcs;
BEGIN{
my $symbol_table = $::{'Foo::'}{'Bar::'}{'Stix::'};
for my $name (qw' a b c '){
$funcs{$name} = *{ $symbol_table->{$name} }{'CODE'};
}
}
I wouldn't do this unless you need the subroutines to have both a fully qualified name, and access to it through a hash variable.
If you only need access to the subroutines through a hash variable this is a better way to set it up.
my %funcs = (
'a' => sub{ print "sub a\n" },
'b' => sub{ print "sub b\n" },
'c' => sub{ print "sub c\n" },
);
Note: you could replace "my %funcs" with "our %funcs"