In Perl, how to dereference temporary hash passed as argument to function?
MyFunct({
Param1 => "knob1",
Param2 => "knob2"
});
# this part never seems to work...
sub MyFunct {
my %param = %{shift()};
my $p1 = $param{Param1};
print "p1: $p1\n";
}
Your code works as is.
$ perl -e'
MyFunct({
Param1 => "knob1",
Param2 => "knob2"
});
# this part never seems to work...
sub MyFunct {
my %param = %{shift()};
my $p1 = $param{Param1};
print "p1: $p1\n";
}
'
p1: knob1
That said, you are needlessly making a copy of the referenced hash. The following is a better approach:
$ perl -e'
MyFunct({
Param1 => "knob1",
Param2 => "knob2"
});
sub MyFunct {
my $param = shift;
my $p1 = $param->{Param1};
print "p1: $p1\n";
}
'
p1: knob1
In addition to #ikegami's answer, I'll add that perhaps you do not need a reference. Passing in the values as key/value pairs will make it easy to convert the list into a hash, using the implicit argument array #_.
The key/value pairs can be performed by using the arrow => delimiter, or in its place a standard comma , delimiter. Below is an example using the arrow.
IDEOne Example
#!/usr/bin/perl
MyFunct(
Param1 => "knob1",
Param2 => "knob2"
);
# this part never seems to work...
sub MyFunct {
my %param = #_;
my $p1 = $param{Param1};
local $\ = "\n";
print "p1: $p1";
print "p2: $param{Param2}";
}
Related
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)
I have a sub that reads a FASTA text file in chunks.
sub reader {
foreach my $line (<IN>) { # read line by line
chomp $line;
if ($line =~ m/^>/) { # if it's a title
&initiator($title, $seq) unless $firsttitle == 1;
$firsttitle = 0;
($title = $line) =~ s/^>//; # title without > at start
$seq = ''; # new seq
} else {
$seq = $seq . $line; # append seq lines
}
}
&initiator($title, $seq); # Do the thing for the last seq.
}
In the middle of several loops, &initiator is called. I'd like to have this in a module that I can "use" but substitute &initiator with other subs from other modules. These subs will need to have their own inputs as well. Would something like the following work or is there a more elegant solution?
use Reader qw(reader);
use Othersub qw(subroutine);
my #par = ('Mary', 'Lamb');
my %functions = (foo => \&Othersub::subroutine);
&reader($file_to_read, $functions{'foo'}($par[0], $par[1]));
Note: Final file structure is Othersub.pm, Reader.pm and the script that uses both modules.
Perl allows you to create references to things, and that includes both subroutines and arrays.
If you've got differing arguments to pass, then I would suggest you want to do so via array reference rather than what you're doing. A bit like this:
use strict;
use warnings;
sub variable_args {
my ( $code_ref, $array_ref ) = #_;
#dereference code ref;
#dereference array ref;
&$code_ref( #$array_ref, "optional", "extra", "arg" );
}
sub foo_func {
foreach (#_) {
print "Foo $_\n";
}
}
sub bar_func {
print "BAR: ", join( ":", #_ ), "\n";
}
#could inline the functions as anonymous subs. I would avoid doing that
#unless they're pretty short/clear.
my %functions = (
'foo' => \&foo_func,
'bar' => \&bar_func,
);
my %args_to_pass = (
'foo' => [ "Mary", "Lamb" ],
'bar' => [ "Some", "Fish", "Pie" ],
);
for my $thing ( "foo", "bar" ) {
variable_args( $functions{$thing}, $args_to_pass{$thing} );
}
Note - in the example above, you call &initiator. You shouldn't do this. It's deprecated syntax from Perl 4, and is redundant (and may have some undesired consequences in certain scenarios).
But I would suggest doing it this way rather than the way you've got. You could get this to work:
&reader($file_to_read, $functions{'foo'}($par[0], $par[1]));
But what will happen when you try and do that is you'll (potentially) just run your function immediately, and pass it's result into reader.
E.g.:
variable_args ( &{$functions{'foo'}}("Mary", "Lamb"), ["more stuff"] );
Won't work, because you're 'running' it immediately, and then sending the result - which'll make your $code_ref whatever the result of the subroutine was.
However you could make an anonymous sub, and pass that:
variable_args( sub {
&{ $functions{'foo'} }( "Special", "Argument", #_ )
},
$args_to_pass{'foo'} );
I would suggest you're getting needlessly convoluted by that point though :)
As far as I can guess, you want to pass a function (reference) as parameter.
Something like this should work for you
# Script
use Reader qw(reader);
use Othersub qw(subroutine);
my #par = .... ;
reader( $file_to_read , \&subroutine , #par);
# Reader.pm
sub reader {
my $file = shift;
my $initiator = shift;
my #par = #_;
...
$initiator->( $file , #par)
...
}
Remark: In the last line of your code, you are not passing the function subroutine to reader, as you might have intendend; instead, you invoke it and pass the result of soubroutine, given the paramers par to reader.
How can i parse all the arbitrary arguments to a hash without specifying the argument names inside my perl script.
Running command with below argument should give hash like below.
-arg1=first --arg2=second -arg3 -arg4=2.0013 -arg5=100
{
'arg2' => 'second',
'arg1' => 'first',
'arg4' => '2.0013',
'arg3' => 1,
'arg5' => 100
};
This can be achieved using Getopt::Long as below
GetOptions(\%hash,
"arg1=s",
"arg2=s",
"arg3",
"arg4=f",
"arg5=i");
However, my argument list is too long and i don't want to specify argument names in GetOptions.
So a call to GetOptions with only hash as a parameter should figure out what arguments are (and their type integer/string/floats/lone arguments) and just create a hash.
There are a lot of Getopt modules. The following are some that will just slurp everything into a hash like you desire:
Getopt::Mini
Getopt::Whatever
Getopt::Casual
I personally would never do something like this though, and have no real world experience with any of these modules. I'd always aim to validate every script for both error checking and as a means to self-document what the script is doing and uses.
Try this:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
sub getOptions {
my (%opts, #args);
while (#_) {
my $opt = shift;
if ($opt =~ /^-/) {
if ($opt =~ /-+([^=]+)(?:=(.+))?/) {
$opts{$1} = $2 ? $2 : 1;
}
}
else {
push #args, $opt;
}
}
return (\%opts, \#args);
}
my ($opts, $args) = getOptions(#ARGV);
print Dumper($opts, $args);
Testing:
$ perl t.pl -arg1=first --arg2=second -arg3 -arg4=2.0013 -arg5=100 datafile
$VAR1 = {
'arg2' => 'second',
'arg1' => 'first',
'arg4' => '2.0013',
'arg3' => 1,
'arg5' => '100'
};
$VAR2 = [
'datafile'
];
This will work as expected for your example,
my %hash = map { s/^-+//; /=/ ? split(/=/, $_, 2) : ($_ =>1) } #ARGV;
so I'm trying to write a subroutine that takes a hash parameter and adds a couple key-value pairs to it (by reference). So far, I've got this:
addParams(\%params);
sub addParams
{
my(%params) = %{$_[0]}; #First argument (as a hash)
$params{"test"} = "testing";
}
But for some reason, It doesn't seem to add the 'test' key. I am new to Perl, but isn't this how you pass a hash by reference? Thanks beforehand.
You can use the hash-ref without de-referencing it:
addParams(\%params);
sub addParams
{
my $params = shift;
$params->{"test"} = "testing";
}
EDIT:
To address your code's issue, when you do:
my(%params) = %{$_[0]};
You're actually making a copy of what the ref points to with %{...}. You can see this via a broken down example (no function, same functionality):
my %hash = ( "foo" => "foo" );
my %copy = %{ \%hash };
$hash{"bar"} = "bar";
$copy{"baz"} = "baz";
print Dumper( \%hash );
print Dumper( \%copy );
Run:
$ ./test.pl
$VAR1 = {
'bar' => 'bar',
'foo' => 'foo'
};
$VAR1 = {
'baz' => 'baz',
'foo' => 'foo'
};
Both hashes have the original 'foo => foo', but now each have their different bar/baz's.
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"