Reputation: 1211
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?
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
.
Upvotes: 1
Views: 173
Reputation: 126742
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
Upvotes: 1
Reputation: 17051
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
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.Upvotes: 1
Reputation: 54373
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.
Upvotes: 3