Perl Using a hash as a reference is deprecated when used with package - perl

I have a module called News (original name, I know) with a method called get_fields, this method returns all the fields that belong to the module like this
sub get_fields {
my $self = shift;
return $self;
}
Now when I call it like this in a different module where I need to do stuff to the fields
my %fields = %{ $news->get_fields };
I discovered doing it like this prevented this issue
Type of argument to keys on reference must be unblessed hashref or
arrayref
when I iterate other fields like this
foreach my $key ( keys %fields ) {
%pairs->{$key} = %fields->{$key} if %fields->{$key};
}
in order to use the values of the fields, I get this warning
Using a hash as a reference is deprecated
which is pointing back to the foreach loop.
How can I avoid this error message without getting the unbless warning back?

I think you're getting mixed up between objects and hashes. get_fields will return $self - which whilst I can't tell for certain, looks like it'll be returning a blessed object.
Now, blessed objects are quite similar to hashes, but they're not the same. You can test the difference with the ref function.
So the question is more - why are you doing this? Why are you trying to cast an object reference into a hash? Because that's what you're doing with:
my %fields = %{ $news->get_fields };
Because pretty fundamentally - even if that worked, it would be a horrible thing to do. The point, purpose and reason for objects is encapsulation - e.g. things outside the module don't meddle with stuff inside.
So why not instead have get_fields return a list of fields, which you can then iterate on and make method calls? This would really be the 'right' way to do something like this.
sub get_fields {
my ( $self ) = #_;
return keys %$self;
}
Or if you really must, embed a method within your object that returns as hash - rather than an object reference - that you can then manipulate externally.
Generally - you don't refer to hashes with a % prefix, unless you're manipulating the whole hash.
To extract a single element from %pairs you should do:
foreach my $key ( keys %pairs ) {
print $pairs{$key},"\n";
}
If the contents of $pairs{$key} is a reference, then you can use the -> to indicate that you should dereference, e.g. $pairs -> {$key}.

Related

Matching optional query parameters using the variable name

I want to create a hash of optional query parameters that are sometimes passed to my subroutine. Sometimes a query parameter called welcome is passed in, and the value is either 1 or 0.
If that variable exists, I want to add it to a hash.
I've created a configuration value called OPTIONAL_URL_PARAMS which is a list of expected parameter names that can be passed in:
use constant OPTIONAL_URL_PARAMS => ("welcome")
So far I have:
my $tempParams = {};
if ( $optionalParam ) {
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
if ($optionalParam eq $self->{$param}) {
$tempParams->{$param} = $optionalParam;
$tempParams->{$param} =~ s/\s+//g; # strip whitespace
}
}
}
But this tries to use the value of $self->{$param} instead of its name. i.e. I want welcome to match welcome, but it's trying to match 1 instead.
I know when it comes to keys in a hash you can use keys %hash, but is there a way you can do this with regular variables?
Edit
My subroutine is being called indirectly:
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
If $optionalParam's variable name is 'welcome', then I want to try and map it to the constant welcome.
This is not an answer any more, but I cannot remove it yet as there is still a discussion going on to clarify the question.
foreach my $param (#{&OPTIONAL_URL_PARAMS}) {
# ...
}
The return value of OPTIONAL_URL_PARAMS (you already got an error here and that's why you have the &, that should have told you something...) is simply a list, not an array ref. Actually at this point it should throw an error because you cannot use 1 as an array reference.
Edit
In Perl, when you pass arguments to a subroutine, all the values are flattened into a single list (reference). Specifically, if you are passing parameters to a sub, the sub doesn't know the names of the variables you originally used. It only knows their values. Therefore, if you want names as well as values, you have to pass them separately. An easy way is using a hash. E.g., in new():
my $class = shift;
my $param = shift; # The first, required parameter
my %therest = (#_); # Optional parameters, if any
Then you can say URL::Handler->new($param, 'welcome' => 1), and $therest{welcome} will have the value 1. You can also say
URL::Handler->new($param, 'welcome'=>1, 'another'=>42);
and %therest will have the two keys welcome and another.
See also some further discussion of passing whole hashes as parameters
Original
This also probably doesn't answer the question!
Some thoughts on the code from your comment.
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift; # OK; refers to URL::Handler
my $args = #_; # Problematic: now $args is the _number_ of args passed (list #_ evaluated in scalar context).
my $self = {
# There is no %args hash, so the next two lines are not doing what you expect.
# I actually don't know enough perl to know what they do! :)
param => $args{param},
optionalParams => $args{optionalParam}
};
}
Some thoughts:
use strict; and use warnings; at the top of your source file, if you haven't yet.
I can think of no languages other than Algol 60 that support this idea. It goes against the idea of encapsulation, and prevents you from using an array or hash element, a function call, a constant, or an expression as the actual parameter to a call
Variable names are purely for the convenience of the programmer and have no part in the functionality of any running program. If you wished so, you could write your code using a single array #memory and have "variables" $memory[0], $memory[1] etc. But you would be bypassing the most useful part of compiler technology that allows us to relate readable text to memory locations. It is best to consider those names to be lost once the program is running
The called code should be interested only in the values passed, and it would be a nightmare if the name of a variable passed as an actual parameter were significant within the subroutine
If you were able to access the name of a variable passed as a parameter, what do you suppose would be provided to subroutine stats if the call looked like this
stats( ( $base[$i] + 0.6 + sum(#offset{qw/ x y /}) + sum(#aa) ) / #aa )
In summary, it cannot be done in general. If you need to associate a value with a name then you should probably be looking at hashes
Your code
my $url_handler = URL::Handler->new($param, $optionalParam);
sub new {
my $class = shift;
my $args = #_;
my $self = {
param => $args{param},
optionalParams => $args{optionalParam}
};
}
has a number of problems
You correctly shift the name of the class from parameter array #_, but then set my $args = #_, which sets $args to the number of elements remaining in #_. But the value of $args is irrelevant because you never use it again
You then set $self to a new anonymous hash, which is created with two elements, using the values from hash %args. But %args doesn't exist, so the value of both elements will be undef. Had you put use strict and use warnings 'all' in place you would have been alerted to this
The keys that you're using to access this non-existent hash are param and optionalParam, and I think it's more than a coincidence that they match the names of the actual parameters of the call to new
While Perl is unusual in that it allows programmatic access to its symbol tables, it is an arcane and unrecommended method. Those names are essentially hidden from the program and the programmer and while modules like Exporter must manipulate symbol tables to do their job, any such behaviour inside base-level software is very much to be avoided
Finally, you never use $self again after defining it. You should be blessing it into a class according to the $class variable (which contains the string URL::Handler) and returning it from the constructor
I hope this helps

Perl dereferencing a subroutine

I have come across code with the following syntax:
$a -> mysub($b);
And after looking into it I am still struggling to figure out what it means. Any help would be greatly appreciated, thanks!
What you have encountered is object oriented perl.
it's documented in perlobj. The principle is fairly simple though - an object is a sort of super-hash, which as well as data, also includes built in code.
The advantage of this, is that your data structure 'knows what to do' with it's contents. At a basic level, that's just validate data - so you can make a hash that rejects "incorrect" input.
But it allows you to do considerably more complicated things. The real point of it is encapsulation, such that I can write a module, and you can make use of it without really having to care what's going on inside it - only the mechanisms for driving it.
So a really basic example might look like this:
#!/usr/bin/env perl
use strict;
use warnings;
package MyObject;
#define new object
sub new {
my ($class) = #_;
my $self = {};
$self->{count} = 0;
bless( $self, $class );
return $self;
}
#method within the object
sub mysub {
my ( $self, $new_count ) = #_;
$self->{count} += $new_count;
print "Internal counter: ", $self->{count}, "\n";
}
package main;
#create a new instance of `MyObject`.
my $obj = MyObject->new();
#call the method,
$obj->mysub(10);
$obj->mysub(10);
We define "class" which is a description of how the object 'works'. In this, class, we set up a subroutine called mysub - but because it's a class, we refer to it as a "method" - that is, a subroutine that is specifically tied to an object.
We create a new instance of the object (basically the same as my %newhash) and then call the methods within it. If you create multiple objects, they each hold their own internal state, just the same as it would if you created separate hashes.
Also: Don't use $a and $b as variable names. It's dirty. Both because single var names are wrong, but also because these two in particular are used for sort.
That's a method call. $a is the invocant (a class name or an object), mysub is the method name, and $b is an argument. You should proceed to read perlootut which explains all of this.

HASH element from an object's return in a single string

I have a module Module.pm with function getHash() which returns, as you guessed, a hash of data)). We all know well how to get an element form that hash, using a standard model:
use Module;
my $m = new Module;
my %hash = $m->getHash();
print $hash{needed_element's_key};
everything is ok.
But what if you need to write two last strings in a single string WITHOUT initializing a new HASH (and occupying a memory for a whole hash, using just one element)?
Is it possible to do so somehow? Like, say, $m->getHash()->{needed_element's_key};
Of course, the given example does not work.
Any suggestions?
Thanks!
It's impossible to return a hash. Subs can only return a list of scalars. You are assigning that list of scalars to an existing hash my %hash and getting an element of that hash.
You could disguise the fact that you are creating a new hash by using an anonymous hash:
my $val = { $m->getKeyValPairs() }->{$key};
Alternatively, a custom filter could be more efficient:
sub find_val {
my $target_key = shift;
while (#_) {
my $key = shift;
my $val = shift;
return $val if $key eq $target_key;
}
return undef;
}
my $val = find_val $key, $m->getKeyValPairs();
But the best option would be to have the method return a reference to a hash instead. Then, you wouldn't have to perform the wasteful activity of creating a hash. You'd simply use the following:
$m->getHashRef()->{$key}
If a sub does return %hash, it actually returns a list of keys and values (alternating). You can put it back in a hash and get a specific key with:
print +{ $m->getHash() }->{needed_element_key};
(The leading + is needed to prevent perl from thinking the { is a block returning a filehandle to print to.)
But you are better off just returning a hash reference.

easy rule about how to access the nested structures in perl

While using the nested structures in perl, I found it a little difficult for me to write right code to access an element in the nested structures, especially which symbol I should use, for example:
my %data = {
name => 'G1',
people => {
lead => 'Tom',
},
products => ['p1', 'p2'],
};
print "Want Tom: ${$data{'people'}}{'lead'}\n";
perl test.pl
Want Tom:
How can I get 'Tom' in this nested structures? or how can I understand that better to know when use what symbols to do that?
The -> syntactic sugar makes it easy to understand your structure:
use strict;
use warnings;
use feature qw(say);
use Data::Dumper;
my %data = ( # Hashes use (...) and not {...}
name => 'G1',
people => {
lead => 'Tom',
},
products => ['p1', 'p2'],
);
say "Tom is " . $data{people}->{lead};
First, when you define a hash, you use (...). The {...} defines a hash reference. What you had wasn't strictly legal. You could have done this:
my $data = { # This is now a hash reference!
name => 'G1',
people => {
lead => 'Tom',
},
products => ['p1', 'p2'],
};
say "Tom is " . $data->{people}->{lead};
Instead of using %data = (...), this uses $data = {...}. Note that $data is a scalar that contains a hash reference! Note how this uses $data->{people}... instead of $data{people}.
Think of -> as meaning points to. $data points to a hash that has the key {people}. This points to another hash that has the key lead.
Since you know references, you should take the next step and learn Object Oriented Perl. You can think of Object Oriented Perl as mere syntactic sugar over references. You basically use subroutines (called methods in Object Oriented Programming) to help set and get the data in your object
Here's your program again rewritten in OO Perl. Note how your complex data structure is a lot easier to manipulate because we let the subroutines... I mean the methods make manipulating the structure much easier to track.
use strict;
use warnings;
use autodie;
use feature qw(say);
my $g1 = Local::Project->new( 'G1' );
$g1->person( 'lead', 'Tom' );
$g1->product( 'p1' );
$g1->product( 'p2' );
say "The name of the project is " . $g1->name;
say "The lead of the project is " . $g1->person('lead');
say "The products on the project are " . $g1->product;
package Local::Project;
sub new {
my $class = shift;
my $name = shift;
my $self = {};
bless $self, $class;
$self->name($name);
return $self;
}
sub name {
my $self = shift;
my $name = shift;
if ( defined $name ) {
$self->{NAME} = $name;
}
return $self->{NAME};
}
sub person {
my $self = shift;
my $title = shift;
my $name = shift;
if ( defined $name ) {
$self->{PEOPLE}->{$title} = $name;
}
return $self->{PEOPLE}->{$title};
}
sub product {
my $self = shift;
my $product = shift;
$self->{PRODUCT} = [] if not defined $self->{PRODUCT};
if ( defined $product ) {
push #{ $self->{PRODUCT} }, $product;
}
# return #{ $self->{PRODUCT} } -- Let's give the user a choice!
my #products = #{ $self->{PRODUCT} };
return wantarray ? #products : join ", ", #products;
}
UPDATE: I thought it would be nice to return a project name. Plus, I added the ability to return the products in a list as a comma separated string. This makes my main program a bit easier to follow. I don't have to join the returned array. I can simply ask my method to do it for me.
You'll probably want methods to track whether a product is in the project, be able to remove people and projects from the structure etc. However, this points out another great thing about OO Perl. You can change how the class itself works, but the program itself doesn't have to be modified.
For example, it would be nice to know if a product is in a project. It would be nice then to use a hash to store my products instead of an array. I could modify my product method in my class to use a hash, and I don't have to modify my program itself. If I used raw references, I'd be going though my entire program looking for where I use an array for my products and changing it to a hash.
You should note that you are using the wrong format to assign to a hash:
my %data = {
# ^--- WRONG!
When assigning to a hash, use parentheses:
my %data = (
Using { ... } will return a reference to a hash, which is a scalar value. It will give you the warning Reference found where even-sized list expected if you have use warnings enabled (which you always should). The resulting structure will look like this:
my %data = ( "HASH(0x50daf8)" => undef );
Meaning the reference was stringified, and since the assignment only had one value, the value of this key is undefined.
You can use this if your intent is to use a hash reference:
my $href = { ....
Explanation
This statement:
${$test{'people'}}{'lead'}
Means this:
${ ... } # this is a dereference of a reference
Which means that:
$test{'people'}
Must return a reference to a hash. Meaning it is at least two-dimensional.
You then take one of the keys from the dereferenced hash:
${ .... }{'lead'}
This should work, although it is a circumspect way of writing. Note that your code has two problems mentioned above, which is why it does not work.
Solution
Besides assigning to your hash the correct way, using parentheses, and using the correct name of your variable, this is how you take a value from a hash, at its simplest:
$data{people}{lead}
{} returns a hash reference. You shouldn't assign a hash reference to a hash (%data), you should use a normal parentheses ().
$data{people}{lead} should return "Tom" (untested).
As #TLP mentioned, use strict and warnings to avoid such problems.
Perhaps this will help. Looking at your data
my %data = (
name => 'G1',
people => {
lead => 'Tom',
},
products => ['p1', 'p2'],
);
The hash %data has three elements, with keys name, people, and products. Their values are
$data{name}, which is a scalar string. It isn't a reference, and so cannot be accessed further.
$data{people}, which is a hash reference, and so there is another level to the structure. The inner hash has just one key, lead, and its value is accessed by adding another {...} index to the hash reference:
$data{people}{lead}
This leads to the scalar string Tom, and so cannot be accessed further.
$data{products}, which is an array reference, and so there is another level to the structure. The inner array has two indexes, 0 and 1, and their values are accessed by adding another [...] index to the array reference
$data{products}[0]
and
$data{products}[1]
These lead to the scalar strings p1 and p2, and so cannot be accessed further.
I hope it is clear from this how to access the values of any arbitrarily nested data structure.
It is also worth explaining that it can often be advantageous to extract intermediate references to temporary scalar variables, especially if you are using nested loops. For instance, you could write
my $products = $data{products};
after which, instead of listing all the keys from the top of the data structure each time, as above, you can say just $products->[0] and $products->[1]. Note the syntax for accessing arrays and hashes through their references uses the arrow indirection operator. You could similarly write
my $people = $data{people};
and then
print $people->{lead}, "\n";
I hope this helps.
[The question has already been answered. This is just some additional information for the OP]
${ EXPR }{lead}
is a hash element dereference. It can also be written as
EXPR->{lead}
so
${$test{people}}{lead}
can also be written as
$test{people}->{lead}
-> can be omitted between two indexes, so the above could also be written as
$test{people}{lead}

About using an array of functions in Perl

We are trying to build an API to support commit() and rollback() automatically, so that we don't have to bother with it anymore. By researching, we have found that using eval {} is the way to go.
For eval {} to know what to do, I have thought of giving the API an array of functions, which it can execute with a foreach without the API having to intepret anything. However, this function might be in a different package.
Let me clarify with an example:
sub handler {
use OSA::SQL;
use OSA::ourAPI;
my #functions = ();
push(#functions, OSA::SQL->add_page($date, $stuff, $foo, $bar));
my $API = OSA::ourAPI->connect();
$API->exec_multi(#functions);
}
The question is: Is it possible to execute the functions in #functions inside of OSA::ourAPI, even if ourAPI has no use OSA::SQL. If not, would it be possible if I use an array reference instead of an array, given that the pointer would point to the known function inside of the memory?
Note: This is the basic idea that we want to base the more complex final version on.
You are NOT adding a function pointer to your array. You are adding teh return value of calling the add_page() subroutine. You have 3 solutions to this:
A. You will need to store (in #functions) an array of arrayrefs of the form [\&OSA::SQL::add_page, #argument_values], meaning you pass in an actual reference to a subroutine (called statically); and then exec_multi will do something like (syntax may not be 100% correct as it's 4am here)
sub exec_multi {
my ($class, $funcs)= #_;
foreach my $f (#$funcs) {
my ($func, #args) = #$f;
my $res = &$func(#args);
print "RES:$res\n";
}
}
Just to re-iterate, this will call individual subs in static version (OSA::SQL::add_page), e.g. WITHOUT passing the package name as the first parameter as a class call OSA::SQL->add_page would. If you want the latter, see the next solution.
B. If you want to call your subs in class context (like in your example, in other words with the class name as a first parameter), you can use ysth's suggestion in the comment.
You will need to store (in #functions) an array of arrayrefs of the form [sub { OSA::SQL->add_page(#argument_values) }], meaning you pass in a reference to a subroutine which will in turn call what you need; and then exec_multi will do something like (syntax may not be 100% correct as it's 4am here)
sub exec_multi {
my ($class, $funcs)= #_;
foreach my $f (#$funcs) {
my ($func) = #$f;
my $res = &$func();
print "RES:$res\n";
}
}
C. You will need to store (in #functions) an array of arrayrefs of the form [ "OSA::SQL", "add_page", #argument_values], meaning you pass in a package and function name; and then exec_multi will do something like (syntax may not be 100% correct as it's 4am here)
my ($package, $sub, #args) = #{ $functions[$i] };
no strict 'refs';
$package->$sub(#args);
use strict 'refs';
If I understood your question correctly, then you don't need to worry about whether ourAPI uses OSA::SQL, since your main code imports it already.
However, since - in #1B - you will be passing a list of packages to exec_multi as first elements of each arrayref, you can do "require $package; $package->import();" in exec_multi. But again, it's completely un-necessary if your handler call already required and loaded each of those packages. And to do it right you need to pass in a list of parameters to import() as well. BUT WHYYYYYY? :)