Unable to pass a hash and a string to a function, together in perl! - 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;
}

Related

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

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)

How to use refernce concept and access element of subroutine argument using Perl?

I am writing a code for calling a subroutine which has 4 argument(3 hashes and one file handler).i want to know how to access them in subroutine.My code is as below.
#print OUTFILE "Content of TPC file:.\n";
my $DATA_INFO = $ARGV[0];
my $OUT_DIR = $ARGV[1];
my $log= "$OUT_DIR/log1";
open(LOG1,">$log");
require "$DATA_INFO";
my $SCRIPT_DIR = $ENV{"SCRIPT_DIR"} ;
require "$SCRIPT_DIR/cmp_fault.pl";
require "$SCRIPT_DIR/pattern_mismatch.pl";
require "$SCRIPT_DIR/scan_count.pl";
print "\nComparing data:\n\n" ;
pattern_mismatch("\%data","\%VAR1","\%status",*LOG1);
cmp_fault("\%data","\%VAR1","\%status",*LOG1);
scan_count("\%data","\%status",*LOG1);
print "\n Comparison done:\n";
foreach $pattern (keys %status) {
print "pattern";
foreach $attr (keys %{$status{$pattern}}) {
print ",$attr";
}
print "\n";
last;
}
#Print Data
foreach $pattern (keys %status) {
print "$pattern";
foreach $attr (keys %{$status{$pattern}}) {
print ",$status{$pattern}{$attr}";
}
print "\n";
Sub routine cmp_fault is here:
sub cmp_fault {
use strict;
use warning;
$data_ref= $_[0];;
$VAR1_ref= $_[1];
$status_ref = $_[2];
$log1_ref=$_[3];
# print LOG1"For TPC : First find the pattern and then its fault type\n";
for $pat ( keys %$data_ref ) {
print "fgh:\n$pat,";
for $key (keys %{$data_ref{$pat}}) {
if($key=~/fault/){
print LOG1 "$key:$data_ref{$pat}{$key},\n";
}
}
}
# print LOG1 "\nFor XLS : First find the pattern and then its pattern type\n";
for $sheet (keys %$VAR1_ref){
if ("$sheet" eq "ATPG") {
for $row (1 .. $#{$VAR1_ref->{$sheet}}) {
$patname = $VAR1_ref->{'ATPG'}[$row]{'Pattern'} ;
next if ("$patname" eq "") ;
$faultXls = $VAR1_ref->{'ATPG'}[$row]{'FaultType'} ;
# print LOG1 " $patname==>$faultXls \n";
if (defined $data{$patname}{'fault'}) {
$faultTpc = $data{$patname}{'fault'} ;
# print LOG1 "\n $patname :XLS: $faultXls :TPC: $faultTpc\n";
if("$faultXls" eq "$faultTpc") {
print LOG1 "PASS: FaultType Matched $patname :XLS: $faultXls :TPC: $faultTpc\n\n\n";
print "PASS: FaultType Matched $patname :XLS: $faultXls :TPC: $faultTpc\n\n";
$status_ref->{$patname}{'FaultType'} = PASS;
}
else {
print LOG1 "FAIL: FaultType Doesn't Match\n\n";
$status_ref->{$patname}{'FaultType'} = Fail;
}
}
}
}
}
}
return 1;
When passing parameters into an array, you can only ever pass a single list of parameters.
For scalars, this isn't a problem. If all you're acting on is a single array, this also isn't a problem.
If you need to send scalars and an array or hash, then the easy way is to 'extract' the scalar parameters first, and then treat 'everything else' as the list.
use strict;
use warnings;
sub scalars_and_array {
my ( $first, $second, #rest ) = #_;
print "$first, $second, ", join( ":", #rest ), "\n";
}
scalars_and_array( "1", "2", "3", 4, 5, 6 );
But it should be noted that by doing so - you're passing values. You can do this with hashes too.
To pass data structure references, it's as you note - pass by reference, then dereference. It's useful to be aware though, that -> becomes useful, because it's accessing a hash and dereferencing it.
use strict;
use warnings;
use Data::Dumper;
sub pass_hash {
my ( $hashref ) = #_;
print $hashref,"\n";
print $hashref -> {"one"},"\n";
print $hashref -> {"fish"} -> {"haddock"};
}
my %test_hash = ( "one" => 2,
"three" => 4,
"fish" => { "haddock" => "plaice" }, );
pass_hash ( \%test_hash );
print "\n";
print Dumper \%test_hash;
The core of your problem here though, is that you haven't turned on strict and warnings which would tell you that:
for $pat ( keys %data_ref ) {
is wrong - there is no hash called data_ref there's only a scalar (which holds a hash reference) called $data_ref.
You need %$data_ref here.
And here:
for $key ( keys %{ $data{$pat} } ) {
You also have no $data - your code says $data_ref. (You might have %data in scope, but that's a really bad idea to mess around with within a sub).
There's a bunch of other errors - which would also be revealed by strict and warnings. That's a very basic debugging step, and you will generally get a much better response from Stack Overflow if you do this before asking for assistance. So please - do that, tidy up your code and remove errors/warnings. If you are still having problems after that, then by all means make a post outlining where and what problem you're having.

Perl: How to turn array into nested hash keys

I need to convert a flat list of keys into a nested hash, as follow:
my $hash = {};
my #array = qw(key1 key2 lastKey Value);
ToNestedHash($hash, #array);
Would do this:
$hash{'key1'}{'key2'}{'lastKey'} = "Value";
sub to_nested_hash {
my $ref = \shift;
my $h = $$ref;
my $value = pop;
$ref = \$$ref->{ $_ } foreach #_;
$$ref = $value;
return $h;
}
Explanation:
Take the first value as a hashref
Take the last value as the value to be assigned
The rest are keys.
Then create a SCALAR reference to the base hash.
Repeatedly:
Dereference the pointer to get the hash (first time) or autovivify the pointer as a hash
Get the hash slot for the key
And assign the scalar reference to the hash slot.
( Next time around this will autovivify to the indicated hash ).
Finally, with the reference to the innermost slot, assign the value.
We know:
That the occupants of a hash or array can only be a scalar or reference.
That a reference is a scalar of sorts. (my $h = {}; my $a = [];).
So, \$h->{ $key } is a reference to a scalar slot on the heap, perhaps autovivified.
That a "level" of a nested hash can be autovivified to a hash reference if we address it as so.
It might be more explicit to do this:
foreach my $key ( #_ ) {
my $lvl = $$ref = {};
$ref = \$lvl->{ $key };
}
But owing to repeated use of these reference idioms, I wrote that line totally as it was and tested it before posting, without error.
As for alternatives, the following version is "easier" (to think up)
sub to_nested_hash {
$_[0] //= {};
my $h = shift;
my $value = pop;
eval '$h'.(join '', map "->{\$_[$i]}", 0..$#_).' = $value';
return $h;
}
But about 6-7 times slower.
I reckon this code is better - more amenable to moving into a class method, and optionally setting a value, depending on the supplied parameters. Otherwise the selected answer is neat.
#!/usr/bin/env perl
use strict;
use warnings;
use YAML;
my $hash = {};
my #array = qw(key1 key2 lastKey);
my $val = [qw/some arbitrary data/];
print Dump to_nested_hash($hash, \#array, $val);
print Dump to_nested_hash($hash, \#array);
sub to_nested_hash {
my ($hash, $array, $val) = #_;
my $ref = \$hash;
my #path = #$array;
print "ref: $ref\n";
my $h = $$ref;
$ref = \$$ref->{ $_ } foreach #path;
$$ref = $val if $val;
return $h;
}
Thxs for the good stuff!!!
I did it the recursive way:
sub Hash2Array
{
my $this = shift;
my $hash = shift;
my #array;
foreach my $k(sort keys %$hash)
{
my $v = $hash->{$k};
push #array,
ref $v eq "HASH" ? $this->Hash2Array($v, #_, $k) : [ #_, $k, $v ];
}
return #array;
}
It would be interesting to have a performance comparison between all of these solutions...
Made a better version of axeman's i think. Easier to understand without the -> and the \shift to me at least. 3 lines without a subroutine.
With subroutine
sub to_nested_hash {
my $h=shift;
my($ref,$value)=(\$h,pop);
$ref=\%{$$ref}{$_} foreach(#_);
$$ref=$value;
return $h;
}
my $z={};
to_nested_hash($z,1,2,3,'testing123');
Without subroutine
my $z={};
my $ref=\$z; #scalar reference of a variable which contains a hash reference
$ref=\%{$$ref}{$_} foreach(1,2,3); #keys
$$ref='testing123'; #value
#with %z hash variable just do double backslash to get the scalar reference
#my $ref=\\%z;
Result:
$VAR1 = {
'1' => {
'2' => {
'3' => 'testing123'
}
}
};

Perl - assign a hash ref in a hash

use 5.010;
use strict;
use warnings;
use JSON::XS;
use YAML::XS;
my %data = ();
my $content = <<HERE;
{
"name":"BLAHBLAH","contact":{"phone":"12345","twitter":"BLAHBLAH"},
"location": {"address":"NOTTELLING","lat":10,"lng":10,"postalCode":"1234",
"city":"BLAH","state":"BLAH","country":"BLAH"},
"categories":[{"id":"BLAH","name":"BLAH"}]
}
HERE
my $id = "name1";
sub function {
my ( $id, $data, $content ) = #_;
my %data = %$data;
my $out = decode_json($content);
say "out:", Dump $out;
$data{$id} = $out;
}
function( $id, \%data, $content );
say "data:", Dump %data;
This doesn't work as the way I expected. Can you please tell me why and how it will work?
"This doesn't work as the way i expected."
What were you expecting? Let's step through the errors:
1) date != data
2) $content=~m!(,*)! will leave $1 empty, since $content doesn't contain any commas.
3) decode_json($1) will throw a runtime error, since $1 is empty and decode_json() can only be applied to a properly formatted JSON string.
4) $id is not defined.
"Can you please tell me why and how it will work?"
It won't work, if that isn't clear yet. There are more errors than code there.
"how do I assign a hash ref into hash?"
Use the \ unary reference operator, eg:
my %h = ();
my %h2 = (
a => 10
);
$h{h2} = \%h2;
print $h{h2}->{a};
You can also declare a scalar ($) as a reference to an anonymous (unnamed) hash; here $hr is a reference, the hash itself has no symbol or name associated with it:
my $hr = {
n => 42
};
# as an existing hash member:
$h{h3} = {
x => 666,
# some other examples:
hr => \%h2,
hr2 => {
x => 1024
}
};
Notice curly braces {} used in the declaration instead of (). When you are nesting (anonymous) hashes as with hr2, always use that form.
If you search for perl hash tutorial you'll find more in-depth things.
The reason that you're not finding anything in the package-scoped %data (the one defined just after use YAML::XS) is because you're creating a brand-new and completely independent %data inside of function with the line
my %data = %$data;
This creates a new hash and copies the contents of the hash referenced by $data into it.
Try this instead:
sub function {
my ($id, $data, $content) = #_;
my $out = decode_json($content);
say "out:", Dump $out;
$data->{$id} = $out;
}
I think you have a typo:
function($id,/%data,$content);
must be
function($id,\%data,$content);
and $content is not a reference to %data hash, so in your function you should do:
my %data=%$data; # in place of "my %content=%$content;"

Access to Perl's empty angle "<>" operator from an actual filehandle?

I like to use the nifty perl feature where reading from the empty angle operator <> magically gives your program UNIX filter semantics, but I'd like to be able to access this feature through an actual filehandle (or IO::Handle object, or similar), so that I can do things like pass it into subroutines and such. Is there any way to do this?
This question is particularly hard to google, because searching for "angle operator" and "filehandle" just tells me how to read from filehandles using the angle operator.
From perldoc perlvar:
ARGV
The special filehandle that iterates over command-line filenames in #ARGV. Usually written as the null filehandle in the angle operator <>. Note that currently ARGV only has its magical effect within the <> operator; elsewhere it is just a plain filehandle corresponding to the last file opened by <>. In particular, passing \*ARGV as a parameter to a function that expects a filehandle may not cause your function to automatically read the contents of all the files in #ARGV.
I believe that answers all aspects of your question in that "Hate to say it but it won't do what you want" kind of way. What you could do is make functions that take a list of filenames to open, and do this:
sub takes_filenames (#) {
local #ARGV = #_;
// do stuff with <>
}
But that's probably the best you'll be able to manage.
Expanding on Chris Lutz's idea, here is a very rudimentary implementation:
#!/usr/bin/perl
package My::ARGV::Reader;
use strict; use warnings;
use autodie;
use IO::Handle;
use overload
'<>' => \&reader,
'""' => \&argv,
'0+' => \&input_line_number,
;
sub new {
my $class = shift;
my $self = {
names => [ #_ ],
handles => [],
current_file => 0,
};
bless $self => $class;
}
sub reader {
my $self = shift;
return scalar <STDIN> unless #{ $self->{names}};
my $line;
while ( 1 ) {
my $current = $self->{current_file};
return if $current >= #{ $self->{names} };
my $fh = $self->{handles}->[$current];
unless ( $fh ) {
$self->{handles}->[$current] = $fh = $self->open_file;
}
if( eof $fh ) {
close $fh;
$self->{current_file} = $current + 1;
next;
}
$line = <$fh>;
last;
}
return $line;
}
sub open_file {
my $self = shift;
my $name = $self->{names}->[ $self->{current_file} ];
open my $fh, '<', $name;
return $fh;
}
sub argv {
my $self = shift;
my $name = #{$self->{names}}
? $self->{names}->[ $self->{current_file} ]
: '-'
;
return $name;
}
sub input_line_number {
my $self = shift;
my $fh = #{$self->{names}}
? $self->{handles}->[$self->{current_file}]
: \*STDIN
;
return $fh->input_line_number;
}
which can be used as:
package main;
use strict; use warnings;
my $it = My::ARGV::Reader->new(#ARGV);
echo($it);
sub echo {
my ($it) = #_;
printf "[%s:%d]:%s", $it, +$it, $_ while <$it>;
}
Output:
[file1:1]:bye bye
[file1:2]:hello
[file1:3]:thank you
[file1:4]:no translation
[file1:5]:
[file2:1]:chao
[file2:2]:hola
[file2:3]:gracias
[file2:4]:
It looks like this has already been implemented as Iterator::Diamond. Iterator::Diamond also disables the 2-argument-open magic that perl uses when reading <ARGV>. Even better, it supports reading '-' as STDIN, without enabling all the other magic. In fact, I might use it for that purpose just on single files.