snakespan
snakespan

Reputation: 1211

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.

Upvotes: 1

Views: 173

Answers (3)

Borodin
Borodin

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

cxw
cxw

Reputation: 17051

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.

Upvotes: 1

simbabque
simbabque

Reputation: 54373

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.

Upvotes: 3

Related Questions