Passing variables to a Perl subroutine - perl

I would like to pass the following variables to subroutine mySubroutine, $name, $age and then this multidimensional array:
$name = "jennifer";
$age = 100;
$list[0][0] = "TEST NAME 2";
$list[0][1] = "TEST GROUP 2";
$[0][2] = 10;
$[1][0] = "TEST NAME 2";
$[1][1] = "TEST GROUP 2";
$[1][2] = 2;
Subroutine:
sub mySubroutine
{
}
I have tried $_[0], and #_, but I don't seem to get the variables passed to the subroutine correctly.

There are several ways to do it (like most things in Perl). I personally do it like this:
sub mySubroutine
{
# Get passed arguments
my ($name, $age, $refList) = #_;
# Get the array from the reference
my #list = #{$refList};
# Good to go
}
# You need to pass #list as reference, so you
# put \#list, which is the reference to the array
mySubroutine($name, $age, \#list);

Another option, as long as you are only passing one array, is to pass it normally by value as the last element:
sub scalars_and_one_array {
my $name = shift;
my $age = shift;
foreach my $element (#_)
{
# Do something with the array that was passed in.
}
}
scalars_and_one_array($name,$age,#array);
However, it is most efficient to avoid any additional copy of the array by only using a reference within the sub. This does mean that changes to the array in the sub affect the original, however:
sub array_by_ref {
my $array_ref = shift;
foreach my $element (#$array_ref)
{
# Changing $element changes #original_array!
}
}
array_by_ref(\#original_array);

Another way, which passes the array by reference, but then makes a copy of it to avoid changing the original when you edit it.
sub mySubroutine
{
## Retrieve name
my $name = shift;
## Retrieve age
my $age = shift;
## Retrieve list reference
my $refList = shift;
## De-reference the list's scalar
my #list = #{$refList};
# Good to go
}
## Function call
mySubroutine($name, $age, \#list);
For a better understanding, please refer to perlsub (Perl subroutines).

Related

How to access hash from object

I have written a small class which just got some getter and setter methods. One of those Properties is a hash.
sub getMyData
{
my $objekt = shift;
return $objekt->{MYDATA};
}
sub setMyData
{
my $objekt = shift;
my %myData= shift;
$objekt->{MYDATA} = \%myData;
}
If i set the value like this in another skript which access my class:
my %test;
$test{'apple'}='red';
$objekt = MYNAMESPACE::MYCLASS->new;
$objekt->setMyData(%test);
I thought i can access this value easy via:
my $data = $objekt->getMyData;
print $data{'apple'};
I just get undef value.
Output from Dumper:
Can someone tell me what's wrong here and how i can access getMyData and print the value 'red'?
shift removes and returns the first element of an array. Inside of a subroutine a bare shift operates on #_, which contains a copy of all arguments passed to that subroutine.
What is really happening here is that setMyData is being passed this data:
setMyData($objekt, 'apple', 'red');
The first shift in setMyData removes $objekt from #_
The second shift in setMyData removes 'apple', but since you assign the result of this shift to a Hash it creates a Hash that looks like this: 'apple' => undef
You take a reference to this Hash and store it in the MYDATA key of $objekt
What you really want is to assign the remainder of #_ to your Hash:
sub setMyData {
my $objekt = shift;
my %myData = #_;
# my ($objekt, %myData) = #_; (alternative)
$objekt->{MYDATA} = \%myData;
}
Another option is to instead send a Hash reference to setMyData, which would work with shift:
sub setMyData {
my $objekt = shift;
my $myData_ref = shift
$objekt->{MYDATA} = $myData_ref;
}
$objekt->setMyData(\%test);
You are missing the dereference arrow. Because you put a hashref (\%myData) in, you also get a reference out.
my $data = $objekt->getMyData;
print $data->{'apple'};
# ^
# here
You also need to change the assignment, because you are passing a list to the setter, not a reference. shift is for scalar (single) values, but %test gets turned into a list (many values).
sub setMyData
{
my $objekt = shift;
my %myData = #_;
$objekt->{MYDATA} = \%myData;
}
However, there are a few more issues with your code.

Pass variable to subroutine with different re-action based on the variable past

So lets say I have the following sub-routine which I use to validate user submitted variables. I want to condense the code down as much as possible and use the sub-routine to check all user supplied variables, but each variable passed to the routine will need to be check against a different regex based on what it was prior to the passing as an arguement.
sub mySubroutine
{
# Get passed arguments
my ($unknown) = #_;
while ($unknown !~ m/^\d{3,6}$/) { # Check for valid integer.
# Does Stuff
}
return $unknown;
}
my $name = Bob;
mySubroutine($name);
...
my $age = 20;
mySubroutine($age);
What would be the best way to identify which regex it needs to be compared against. i.e. check $age passed into $unknown against digit only regex, but check $name also passed to $unknown against a string regex.
I am thinking I need to use an additional variable like $type as what is being passed then check that against a dispatch table, but then I would have to pass two variables each time. Is there a way for a subroutine to see what the variable was names that is passed to it from the main namespace?
I don't think this is worded all that well, so please let me know if I need to elaborate or rephrase.
Do you mean
sub mySubroutine
{
# Get passed arguments
my (%args) = #_;
my $regexes = (
age => qr/.../,
name => qr/.../,
);
foreach my $key in (%args)
{
if (%args{$key} !~ /$regexes{$key}/)
{
....
}
}
}
my $name = Bob;
mySubroutine(name => $name);
...
my $age = 20;
mySubroutine(age => $age);
mySubroutine(age => $age, name => $name, );
That way you can solve this, without jumping through too many hoops...
You could in %regexes use array references, if more than one test is necessary, or hash references, if you want to add an error message to each test and then loop through those...
the %regexes hash (or better a reference to it) could also be part of the parameters...
You could pass the regex to the subroutine, something like:
sub mySubroutine {
my ($reg, $str) = #_;
if ($str =~ $reg) {
return $str;
} else {
say "invalid string: $str";
return 0;
}
}
my $name = 'Bob';
# put in the regex all characters you want for a valid name !!!
$name = mySubroutine(qr/^[a-zA-Z' -]+$/,$name);
say $name;
$name = '123';
$name = mySubroutine(qr/^[a-zA-Z' -]+$/,$name);
say $name;
my $age = 20;
$age = mySubroutine(qr/^\d{1,3}$/, $age);
say $age;
$age = 'hgj';
$age = mySubroutine(qr/^\d{1,3}$/, $age);
say $age;
output:
Bob
invalid string: 123
0
20
invalid string: hgj
0

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);

implement a node list in Perl

I wrote the following module but am not sure how to refer to the "last" and "head" nodes. As well as storing the address of the next node in "{nextNode}" in the previous node.
I am trying to save the reference of the class when storing it but later it's complaining: "Not a HASH reference at List.pm"; which I understand why but am not sure how the syntax would be.
If I de-reference $head and $last ($$last->{nextNode} = \$class) then I think it's using the actual name of my class; List and not the previous object like I want to.
package List;
my $head = undef;
my $last = undef;
sub new {
my $class = shift;
# init the head of the list
if ($head == undef) {
$head = \$class;
print "updated head to:$head", "\n";
}
$last = \$class;
$last->{nextNode} = \$class; # update previous node to point on this new one
print "updated last to:$last", "\n";
my $self = {};
$self->{value} = shift;
$self->{nextNode} = ""; # reset next to nothing since this node is last
return bless $self, $class;
}
Thanks guys
You should be storing $self everywhere instead of \$class. Storing $class is simply storing the name of the class, not the object itself.
Also, for $self->{nextNode} I'd store an undef instead of a blank string. Or better yet, simply don't create it at all and use exists when checking if it is there.
You're over thinking it. If you use an array for your list instead of a hash, you don't need to worry about the head and last. The head of an array is $array[0] and the last member is $array[-1]. Simple and easy to do.
Here's a quick standard class definition for defining a list. I've only defined a constructor (the new subroutine) and one method (the list).
package Local::List;
sub new {
my $class = shift;
my $self = {};
bless $self, $class;
$self->list([]);
}
sub list {
my $self = shift;
my $list_ref = shift;
if (ref $list_ref ne "ARRAY) {
return;
}
if (defined $list_ref) {
$self->{LIST} = $list_ref;
}
if wantarray {
return $self->{LIST};
}
}
The first thing: Use the same standard names everyone else uses. Use new for the constructor. When I try to look at the documentation on how to use your class, I can search for the word new and know that's how I create a class object. Also, use the variable names $class and $self. That's what everyone else does, so it's easy to know what's going on.
Notice in my new subroutine, the first item passed is the name of the class while the first item passed to my other subroutines is a reference to my class object (i.e. $self). That's probably the hardest thing to understand about classes.
Notice in new, I immediately create my $self and bless it. That way, I can call my other subroutines (my methods) to do the setting for me. This way, my constructor doesn't know how my class is structured. This has a lot of advantages:
When (not if) I modify my class, I don't have to modify the constructor.
My constructor is always in sync with all of my methods.
I don't have to know how my class object is structured when I start defining the class. I can start writing my class without worrying about all those dirty details on how it'll work.
Notice that the list subroutine (or method) can either set a list or return a list. It's much easier if you use the same subroutine to set or get the value. Also in your method subroutines, use a blank return when your method function returns an error. Otherwise, always return something. That makes it easy to test to see if a method failed or not.
Let's look at some of the other methods you probably want to have. Let's have all the four standard list functions:
push
pop
shift
unshift
Here's an example:
sub push {
my $self = shift;
my $member = shift;
if (not defined $member) {
return;
}
my $list_ref = $self->list;
my $return = push #{ $list_ref }, $member;
$self->list($list_ref);
return $return;
}
Wow, that's simple. Notice that the pop doesn't know what my class looks like. It used the list method to retrieve a list reference. Then it used the builtin push method to push a member onto the list. I save that return value, and that's what I'll return. I'm not even sure what push returns. All I know is that push returns something if it succeeds. (Yes, I know it returns the number of items in the list).
The other three functions are more or less the same. Here's a few more:
current
splice
next
previous
head
last
All you need to do for current is to store the current value. Use the same function to set and get the value. Notice that my list method or my push method, or my new constructor knows or care how you store it. Nor, do our next and previous methods. All they need to do is increment or decrement the value of current and store it back using the current method subroutine:
sub next {
my $self = shift
my #list = $self->list; #Returns a list;
my $current = $self->current;
my $list_size = $#list;
if ($current eq $list_size) {
return; #Can't return a value after the end of the list!
}
$current++; #Increment the value;
my $value = $list[$current]; #I'll return this
$self->current($current) #Store the new current
return $value;
}
And, now to the basis of your question: Getting the last and head values of the list. Here's last
sub last {
my $self = shift;
my $list_ref = $self->list;
return ${ $list_ref }[-1];
}
And a quick copy and paste will give me head:
sub head {
my $self = shift;
my $list_ref = $self->list;
return ${ $list_ref }[0];
}
That's it! All that worrying you were doing was for naught.
Sorry for the long post. I just wanted to emphasize that object oriented programming in Perl isn't that tricky as long as you follow a few simple guide lines.
(Simple? What about use Moose; No, I said simple!). ;-)
I just want to post my final working version for the record and for your feedback/comments.
Thanks again!!
package List;
my $head = undef;
my $last = undef;
sub new {
my ($class, $val) = #_;
my $self = {};
# init the head of the list
if (!defined $head) {
$head = $self;
print "updated the head of the list ($head)" . "\n";
}
else {
$last->{nextNode} = $self; # update previous node to point on this new one
}
$last = $self; # this object is now the last one
$self->{value} = $val; # store the value
$self->{nextNode} = undef; # reset next to nothing since this node is last
return bless $self, $class;
}
sub setVal {
my ($class, $val) = #_;
$class->{value} = $val;
}
sub getVal {
my $class = shift;
print $class->{value};
}
sub getNext {
my $class = shift;
return $class->{nextNode};
}
# return true if this is the last node, otherwise false.
sub isLast {
my $class = shift;
return 1 if !defined $class->{nextNode};
return 0;
}
sub getLast {
return $last;
}
sub getHead {
return $head;
}
# looping through all the list and printing the values
sub showList {
my $node = $head; # set temp node to the head
while ( !$node->isLast() ) {
print $node->{value} . "\n";
$node = $node->{nextNode};
}
# printing last value. (should be defined but I check it just in case)
print $node->{value} . " (last)\n" if defined $node->{value};
}
1;
Script:
my $n0 = new List(4);
my $n1 = new List(8);
my $n2 = new List(9);
my $n3 = new List(3);
my $n4 = new List(1);
my $n5 = new List(0);
my $n6 = new List(5);
print "\nShow list: \n";
$n2->showList(); # any object will print the list

How do you access information in a hash reference that has been passed to a sub-routine?

I am trying to use hash references to pass information to sub-routines. Psuedo code:
sub output_detail {
Here I want to be able to access each record by the key name (ex. "first", "second", etc)
}
sub output_records {
I want to use a foreach to pass each record has reference to another sub-routine
that handles each record.
foreach $key ( sort( keys %someting) ) {
output_detail(something);
}
}
%records = ();
while ($recnum, $first, $second, $third) = db_read($handle)) {
my %rec = ("first"=>$first, "second"=>$second, "third=>$third);
my $id = $recnum;
$records{$id} = \%rec;
}
output_records(\%records);
I'm not sure how to de-reference the hashes when passed to a sub-routine.
Any ideas would be very helpful.
Thanks
Use -> to access keys of a hash ref. So, your argument to output_records will come through as a scalar hash ref.
sub output_records {
my $records = shift;
my $first = $records->{"first"};
}
See perlreftut for more info.
sub output_detail {
my $hash = shift;
my $value = $$hash{some_key};
}
sub output_records {
my $hash = shift;
foreach my $key (sort keys %$hash) {
output_detail($hash, $key);
# or just pass `$$hash{$key}` if you only need the value
}
}