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.
Related
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}";
}
This may be something very simple for you but i have been trying for it for more than one hour.
Ok.. Here is my code,
#collection = [
{
'name' => 'Flo',
'model' => '23423',
'type' => 'Associate',
'id' => '1-23928-2392',
'age' => '23',
},
{
'name' => 'Flo1',
'model' => '23424',
'type' => 'Associate2',
'id' => '1-23928-23922',
'age' => '25',
}];
foreach my $row (#collection) {
$build_status = $self->build_config($row);
}
sub build_config {
my $self = shift;
my %collect_rows = #_;
#my %collect_rows = shift;
print Dumper %collect_rows; exit;
exit;
}
Where $row is really an Hash. But when i Print it.. It gives output like below (I am using Dumper),
$VAR1 = 'HASH(0x20a1d68)';
You are confusing references with hashes and arrays.
An array is declared with:
my #array = (1, 2, 3);
A hash is declared with:
my %hash = (1 => 2, 3 => 4);
That's because both hashes and arrays are simply lists (fancy lists, but I digress). The only time you need to use the [] and {} is when you want to use the values contained in the list, or you want to create a reference of either list (more below).
Note that the => is just a special (ie. fat) comma, that quotes the left-hand side, so although they do the same thing, %h = (a, 1) would break, %h = ("a", 1) works fine, and %h = (a => 1) also works fine because the a gets quoted.
An array reference is declared as such:
my $aref = [1, 2, 3];
...note that you need to put the array reference into a scalar. If you don't and do it like this:
my #array = [1, 2, 3];
... the reference is pushed onto the first element of #array, which is probably not what you want.
A hash reference is declared like this:
my $href = {a => 1, b => 2};
The only time [] is used on an array (not an aref) is when you're using it to use an element: $array[1];. Likewise with hashes, unless it's a reference, you only use {} to get at a key's value: $hash{a}.
Now, to fix your problem, you can keep using the references with these changes:
use warnings;
use strict;
use Data::Dumper;
# declare an array ref with a list of hrefs
my $collection = [
{
'name' => 'Flo',
...
},
{
'name' => 'Flo1',
...
}
];
# dereference $collection with the "circumfix" operator, #{}
# ...note that $row will always be an href (see bottom of post)
foreach my $row (#{ $collection }) {
my $build_status = build_config($row);
print Dumper $build_status;
}
sub build_config {
# shift, as we're only accepting a single param...
# the $row href
my $collect_rows = shift;
return $collect_rows;
}
...or change it up to use non-refs:
my #collection = (
{
'name' => 'Flo',
...
},
{
'name' => 'Flo1',
...
}
);
foreach my $row (#collection) {
my $build_status = build_config($row);
# build_config() accepts a single href ($row)
# and in this case, returns an href as well
print Dumper $build_status;
}
sub build_config {
# we've been passed in a single href ($row)
my $row = shift;
# return an href as a scalar
return $row;
}
I wrote a tutorial on Perl references you may be interested in guide to Perl references. There's also perlreftut.
One last point... the hashes are declared with {} inside of the array because they are indeed hash references. With multi-dimentional data, only the top level of the structure in Perl can contain multiple values. Everything else underneath must be a single scalar value. Therefore, you can have an array of hashes, but technically and literally, it's an array containing scalars where their value is a pointer (reference) to another structure. The pointer/reference is a single value.
How can I create a subroutine that can parse arguments like this:
&mySub(arg1 => 'value1', arg2 => 'value2' ...);
sub mySub() {
# what do I need to do here to parse these arguments?
# no arguments are required
}
Simply assign the input array to a hash:
sub my_sub {
my %args = #_;
# Work with the %args hash, e.g.
print "arg1: ", $args{arg1};
}
If you want to provide default values, you can use:
sub my_sub {
my %args = ( 'arg1' => 'default arg1',
'arg2' => 'default arg2',
#_ );
# Work with the (possibly default) values in %args
}
Maybe you'll also find very useful the Method::Signatures module, which will allow you to do something like that:
func MySub (Str :$arg1 = 'default arg1', Str :$arg2 = 'default arg2') {
print "arg1: ", $arg1};
}
Need help figuring out how to do this. My code:
my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};
&printInfo(%hash);
sub printInfo{
my (%hash) = %_;
foreach my $key (keys %_{
my $a = $_{$key}{'Make'};
my $b = $_{$key}{'Color'};
print "$a $b\n";
}
}
The easy way, which may lead to problems when the code evolves, is simply by assigning the default array #_ (which contains all key-value-pairs as an even list) to the %hash which then rebuilds accordingliy. So your code would look like this:
sub printInfo {
my %hash = #_;
...
}
The better way would be to pass the hash as reference to the subroutine. This way you could still pass more parameters to your subroutine.
printInfo(\%hash);
sub PrintInfo {
my %hash = %{$_[0]};
...
}
An introduction to using references in Perl can be found in the perlreftut
You're so very, very close. There is no %_ for passing hashes, it must be passed in #_. Luckily, Hashes are assigned using a list context, so
sub printInfo {
my %hash = #_;
...
}
will make it work!
Also note, using the & in front of the subroutine call has been, in most cases, unnecessary since at least Perl 5.000. You can call Perl subroutines just like in other languages these days, with just the name and arguments. (As #mob points out in the comments, there are some instances where this is still necessary; see perlsub to understand this more, if interested.)
The best way to pass hashes and arrays is by reference. A reference is simply a way to talk about a complex data structure as a single data point -- something that can be stored in a scalar variable (like $foo).
Read up on references, so you understand how to create a reference and dereference a reference in order to get your original data back.
The very basics: You precede your data structure with a backslash to get the reference to that structure.
my $hash_ref = \%hash;
my $array_ref = \#array;
my $scalar_ref = \$scalar; #Legal, but doesn't do much for you...
A reference is a memory location of the original structure (plus a clue about the structure):
print "$hash_ref\n";
Will print something like:
HASH(0x7f9b0a843708)
To get the reference back into a useable format, you simply put the reference into the correct sigil in front:
my %new_hash = %{ $hash_ref };
You should learn about using references since this is the way you can create extremely complex data structures in Perl, and how Object Oriented Perl works.
Let's say you want to pass three hashes to your subroutine. Here are the three hashes:
my %hash1 = ( this => 1, that => 2, the => 3, other => 4 );
my %hash2 = ( tom => 10, dick => 20, harry => 30 );
my %hash3 = ( no => 100, man => 200, is => 300, an => 400, island => 500 );
I'll create the references for them
my $hash_ref1 = \%hash1;
my $hash_ref2 = \%hash2;
my $hash_ref3 = \%hash3;
And now just pass the references:
mysub ( $hash_ref1, $hash_ref2, $hash_ref3 );
The references are scalar data, so there's no problem passing them to my subroutine:
sub mysub {
my $sub_hash_ref1 = shift;
my $sub_hash_ref2 = shift;
my $sub_hash_ref3 = shift;
Now, I just dereference them, and my subroutine can use them.
my %sub_hash1 = %{ $sub_hash_ref1 };
my %sub_hash2 = %{ $sub_hash_ref2 };
my %sub_hash3 = %{ $sub_hash_ref3 };
You can see what a reference is a reference to by using the ref command:
my $ref_type = ref $sub_hash_ref; # $ref_type is now equal to "HASH"
This is useful if you want to make sure you're being passed the correct type of data structure.
sub mysub {
my $hash_ref = shift;
if ( ref $hash_ref ne "HASH" ) {
croak qq(You need to pass in a hash reference);
}
Also note that these are memory references, so modifying the reference will modify the original hash:
my %hash = (this => 1, is => 2, a => 3 test => 4);
print "$hash{test}\n"; # Printing "4" as expected
sub mysub ( \%hash ); # Passing the reference
print "$hash{test}\n"; # This is printing "foo". See subroutine:
sub mysub {
my $hash_ref = shift;
$hash_ref->{test} = "foo"; This is modifying the original hash!
}
This can be good -- it allows you to modify data passed to the subroutine, or bad -- it allows you to unintentionally modify data passed to the original subroutine.
I believe you want
my %hash;
$hash{'1'}= {'Make' => 'Toyota','Color' => 'Red',};
$hash{'2'}= {'Make' => 'Ford','Color' => 'Blue',};
$hash{'3'}= {'Make' => 'Honda','Color' => 'Yellow',};
printInfo(%hash);
sub printInfo{
my %hash = #_;
foreach my $key (keys %hash){
my $a = $hash{$key}{'Make'};
my $b = $hash{$key}{'Color'};
print "$a $b\n";
}
}
In the line printInfo(%hash) the %hash is expanded to a list with the alternating key-value pairs.
In printInfo, the #_ is this list that, and assigned to %hash it creates again the keys with their corresponding value from the alternating elements in the list.
You can pass them as
The argument list do_hash_thing( %hash )
A reference to the hash in the argument list
`do_hash_thing( #args_before, \%hash, #args_after )
As a reference by prototype, working like keys and other hash operators.
The list works like so:
sub do_hash_thing {
my %hash = #_;
...
}
do_hash_thing( %hash );
This also allows you to "stream" hash arguments as well:
do_hash_thing( %hash_1, %hash_2, parameter => 'green', other => 'pair' );
By reference works like this:
sub do_hash_thing {
my $hash_ref = shift;
...
}
do_hash_thing( \%hash, #other_args );
Here by prototype (\%#). The prototype makes perl look for a hash in the first argument and pass it by reference.
sub do_hash_thing (\%#) {
my $hash_ref = shift;
...
}
do_hash_thing( %hash => qw(other args) );
# OR
do_hash_thing %hash => qw(other args);
Caveat: prototypes don't work on methods.
I need some help regarding the arrays in Perl
This is the constructor I have.
BuildPacket.pm
sub new {
my $class = shift;
my $Packet = {
_PacketName => shift,
_Platform => shift,
_Version => shift,
_IncludePath => [#_],
};
bless $Packet, $class;
return $Packet;
}
sub SetPacketName {
my ( $Packet, $PacketName ) = #_;
$Packet->{_PacketName} = $PacketName if defined($PacketName);
return $Packet->{_PacketName};
}
sub SetIncludePath {
my ( $Packet, #IncludePath ) = #_;
$Packet->{_IncludePath} = \#IncludePath;
}
sub GetPacketName {
my( $Packet ) = #_;
return $Packet->{_PacketName};
}
sub GetIncludePath {
my( $Packet ) = #_;
#{ $Packet->{_IncludePath} };
}
(The code has been modified according to the suggestions from 'gbacon', thank you)
I am pushing the relative paths into 'includeobjects' array in a dynamic way. The includepaths are being read from an xml file and are pushed into this array.
# PacketInput.pm
if($element eq 'Include')
{
while( my( $key, $value ) = each( %attrs ))
{
if($key eq 'Path')
push(#includeobjects, $value);
}
}
So, the includeobject will be this way:
#includeobjects = (
"./input/myMockPacketName",
"./input/myPacket/my3/*.txt",
"./input/myPacket/in.html",
);
I am using this line for set include path
$newPacket->SetIncludePath(#includeobjects);
Also in PacketInput.pm, I have
sub CreateStringPath
{
my $packet = shift;
print "printing packet in CreateStringPath".$packet."\n";
my $append = "";
my #arr = #{$packet->GetIncludePath()};
foreach my $inc (#arr)
{
$append = $append + $inc;
print "print append :".$append."\n";
}
}
I have many packets, so I am looping through each packet
# PacketCreation.pl
my #packets = PacketInput::GetPackets();
foreach my $packet (PacketInput::GetPackets())
{
print "printing packet in loop packet".$packet."\n";
PacketInput::CreateStringPath($packet);
$packet->CreateTar($platform, $input);
$packet->GetValidateOutputFile($platform);
}
The get and set methods work fine for PacketName. But since IncludePath is an array, I could not get it to work, I mean the relative paths are not being printed.
If you enable the strict pragma, the code doesn't even compile:
Global symbol "#_IncludePath" requires explicit package name at Packet.pm line 15.
Global symbol "#_IncludePath" requires explicit package name at Packet.pm line 29.
Global symbol "#_IncludePath" requires explicit package name at Packet.pm line 30.
Global symbol "#_IncludePath" requires explicit package name at Packet.pm line 40.
Don't use # unquoted in your keys because it will confuse the parser. I recommend removing them entirely to avoid confusing human readers of your code.
You seem to want to pull all the attribute values from the arguments to the constructor, so continue peeling off the scalar values with shift, and then everything left must be the include path.
I assume that the components of the include path will be simple scalars and not references; if the latter is the case, then you'll want to make deep copies for safety.
sub new {
my $class = shift;
my $Packet = {
_PacketName => shift,
_Platform => shift,
_Version => shift,
_IncludePath => [ #_ ],
};
bless $Packet, $class;
}
Note that there's no need to store the blessed object in a temporary variable and then immediately return it because of the semantics of Perl subs:
If no return is found and if the last statement is an expression, its value is returned.
The methods below will also make use of this feature.
Given the constructor above, GetIncludePath becomes
sub GetIncludePath {
my( $Packet ) = #_;
my #path = #{ $Packet->{_IncludePath} };
wantarray ? #path : \#path;
}
There are a couple of things going on here. First, note that we're careful to return a copy of the include path rather than a direct reference to the internal array. This way, the user can modify the value returned from GetIncludePath without having to worry about mucking up the packet's state.
The wantarray operator allows a sub to determine the context of its call and respond accordingly. In list context, GetIncludePath will return the list of values in the array. Otherwise, it returns a reference to a copy of the array. This way, client code can call it either as in
foreach my $path (#{ $packet->GetIncludePath }) { ... }
or
foreach my $path ($packet->GetIncludePath) { ... }
SetIncludePath is then
sub SetIncludePath {
my ( $Packet, #IncludePath ) = #_;
$Packet->{_IncludePath} = \#IncludePath;
}
Note that you could have used similar code in the constructor rather than removing one parameter at a time with shift.
You might use the class defined above as in
#! /usr/bin/perl
use strict;
use warnings;
use Packet;
sub print_packet {
my($p) = #_;
print $p->GetPacketName, "\n",
map(" - [$_]\n", $p->GetIncludePath),
"\n";
}
my $p = Packet->new("MyName", "platform", "v1.0", qw/ foo bar baz /);
print_packet $p;
my #includeobjects = (
"./input/myMockPacketName",
"./input/myPacket/my3/*.txt",
"./input/myPacket/in.html",
);
$p->SetIncludePath(#includeobjects);
print_packet $p;
print "In scalar context:\n";
foreach my $path (#{ $p->GetIncludePath }) {
print $path, "\n";
}
Output:
MyName
- [foo]
- [bar]
- [baz]
MyName
- [./input/myMockPacketName]
- [./input/myPacket/my3/*.txt]
- [./input/myPacket/in.html]
In scalar context:
./input/myMockPacketName
./input/myPacket/my3/*.txt
./input/myPacket/in.html
Another way to reduce typing is to use Moose.
package Packet;
use Moose::Policy 'Moose::Policy::JavaAccessors';
use Moose;
has 'PacketName' => (
is => 'rw',
isa => 'Str',
required => 1,
);
has 'Platform' => (
is => 'rw',
isa => 'Str',
required => 1,
);
has 'Version' => (
is => 'rw',
isa => 'Int',
required => 1,
);
has 'IncludePath' => (
is => 'ro',
isa => 'ArrayRef[Str]',
default => sub {[]},
traits => [ 'Array' ],
handles => {
getIncludePath => 'elements',
getIncludePathMember => 'get',
setIncludePathMember => 'set',
},
);
__PACKAGE__->meta->make_immutable;
no Moose;
1;
Check out Moose::Manual::Unsweetened for another example of how Moose saves time.
If you are adamant in your desire to learn classical Perl OOP, read the following perldoc articles: perlboot, perltoot, perlfreftut and perldsc.
A great book about classical Perl OO is Damian Conway's Object Oriented Perl. It will give you a sense of the possibilities in Perl's object.
Once you understand #gbacon's answer, you can save some typing by using Class::Accessor::Fast:
#!/usr/bin/perl
package My::Class;
use strict; use warnings;
use base 'Class::Accessor::Fast';
__PACKAGE__->follow_best_practice;
__PACKAGE__->mk_accessors( qw(
IncludePath
PacketName
Platform
Version
));
use overload '""' => 'to_string';
sub to_string {
my $self = shift;
sprintf(
"%s [ %s:%s ]: %s",
$self->get_PacketName,
$self->get_Platform,
$self->get_Version,
join(':', #{ $self->get_IncludePath })
);
}
my $obj = My::Class->new({
PacketName => 'dummy', Platform => 'Linux'
});
$obj->set_IncludePath([ qw( /home/include /opt/include )]);
$obj->set_Version( '1.05b' );
print "$obj\n";