1

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.

Community
  • 1
  • 1
snakespan
  • 1,183
  • 2
  • 15
  • 33
  • Why is `OPTIONAL_URL_PARAMS` a function? Is that supposed to be a constant? If so, then `use constant` and make it a ref. Constants cannot be lists. They are scalar. So the `("welcome")` gets forced into scalar context, and since it's a list, it gets converted to the _number of elements_ in the list. – simbabque Feb 03 '17 at 12:56
  • How is your subroutine being called? Is it something like `my_sub()` vs `my_sub(welcome => 1)`? If the latter, you can stuff `@_` into a hash first. – cxw Feb 03 '17 at 12:58
  • @simbabque It is a constant. I forgot to include that: use constant OPTIONAL_URL_PARAMS => ('welcome') – snakespan Feb 03 '17 at 13:03
  • If you want to compare `$optionalParam` to `$param` you need to do `if ($optionalParam eq $param)` instead of reaching into `$self`. But still, the rest is not correct. – simbabque Feb 03 '17 at 13:03
  • If it's a constant like that, your code should fail because you are derefencing a `1` as an array. – simbabque Feb 03 '17 at 13:03
  • @cxw My sub 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} } } so basically if $optionalParam's var name is 'welcome', then I want to try and map it to the constant 'welcome'. – snakespan Feb 03 '17 at 13:04
  • 1
    What's in `$self->{$param}`? You're not telling us everything. My answer explains the problem with the `1` and the constant, but I think there is something else weird here. – simbabque Feb 03 '17 at 13:05
  • 1
    @simbabque: *"Constants cannot be lists"* Sure they can! The `constant` pragma implements constants as subroutines, so they can be anything that a subroutine can return. For instance `perl -E "use constant LIST => ( 'a', 'b', 'c'); say for LIST"` – Borodin Feb 03 '17 at 13:31
  • 1
    @Borodin oops. Ok, that makes part of my answer untrue. I'll edit/ – simbabque Feb 03 '17 at 13:32
  • @snakespan: I'm baffled too. None of the code you show contains variables with the names that you mention. It's not helped by the number of bugs in your code. In your `sub new`, `my $args = @_` will set `$args` to the number of elements remaining in `@_` after you have shifted `$class` from the beginning. But then you ignore `$args` and go ahead and write code as if there was a hash `%args`. And you use constant string keys `'param'` and `'optionalParam'` instead of involving the values `$param` and `$optionalParam` from the passed parameters. – Borodin Feb 03 '17 at 13:58
  • I think you're rather lost, and you should take a few steps back and explain the problem you're trying to solve in a new question, together with your design ideas and the code you have written. – Borodin Feb 03 '17 at 14:01

3 Answers3

3

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.

simbabque
  • 53,749
  • 8
  • 73
  • 136
  • That still doesn't answer my question though. Simply put, I want to output the variable name 'welcome' instead of its value. Is there a way in Perl to do that? – snakespan Feb 03 '17 at 13:09
  • @snakespan what variable has a name 'welcome'? `$param` contains the string _welcome_. I don't understand what you want to do. – simbabque Feb 03 '17 at 13:20
  • $param does, you're right, but $optionalParam contains a value. So how do I get the variable name associated with that value? – snakespan Feb 03 '17 at 13:28
  • 1
    @snakespan what variable name? I still don't understand what you want to do. Please try to take a step back and explain in a broader context. – simbabque Feb 03 '17 at 13:31
  • OK, step back. When I create a variable, it stores it in memory as var = 1; Instead of getting the value of 1, I want the var name, so I can match against a list of 'approved URL params' so I can use later. Hope that explains things better :) – snakespan Feb 03 '17 at 13:34
  • @snakespan how do you decide that there is a variable `var`? That's btw not a variable. Perl variables need to have a sigil, so it could be `$var`. – simbabque Feb 03 '17 at 13:35
  • 1
    simbabque, thanks for working with the OP to clarify the question! You have my upvote. – cxw Feb 03 '17 at 13:52
  • Hi, you're focusing on semantics / syntax instead of my argument. I was giving a language agnostic example there. The point has been answered, when you pass a variable to a method, you don't get both its name and value, you only get its value, so its impossible to match against. – snakespan Feb 03 '17 at 13:57
  • 1
    *"language agnostic"* isn't half of it. Your imagined functionality isn't available in any modern language that I know about. I wonder where you got the idea? – Borodin Feb 03 '17 at 15:04
1

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.
Community
  • 1
  • 1
cxw
  • 16,685
  • 2
  • 45
  • 81
  • @snakespan Per your [last comment](http://stackoverflow.com/questions/42024733/perl-matching-optional-query-params-using-variable-name/42025519#comment71224898_42025024), you can't get from a parameter back to the name of the variable it came from. – cxw Feb 03 '17 at 13:36
  • OK, that kind of answers my question actually. I thought when a variable is passed in, even though you're only passing in the value, you're somehow able to pluck the variable name out of memory and use it. I'll use a hash instead which will probably make life easier. Thanks – snakespan Feb 03 '17 at 13:36
  • For the record, I can using strict and warnings, but I though it unnecessary to paste it because it's normally assumed. Appreciate the help. – snakespan Feb 03 '17 at 13:57
  • 2
    *"I though it unnecessary to paste [strict and warnings] because it's normally assumed"* Far from it, sadly. There is still a large number of programmers who avoid `use strict` and `use warnings` (or, worse, write it and comment it out) mainly because they produce errors that they don't want to see! You should also show the shebang line, if you have one, because it may impact on the behaviour of the program. – Borodin Feb 03 '17 at 14:06
1

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

Borodin
  • 126,100
  • 9
  • 70
  • 144