views:

1529

answers:

10

Is there any way to use a constant as a hash key?

For example:

use constant X => 1;

my %x = (X => 'X');

The above code will create a hash with "X" as key and not 1 as key. Whereas, I want to use the value of constant X as key.

+15  A: 

use constant actually makes constant subroutines.

To do what you want, you need to explicitly call the sub:

use constant X => 1;

my %x = ( &X => 'X');

or

use constant X => 1;

my %x = ( X() => 'X');
nohat
+9  A: 

Your problem is that => is a magic comma that automatically quotes the word in front of it. So what you wrote is equivalent to ('X', 'X').

The simplest way is to just use a comma:

my %x = (X, 'X');

Or, you can add various punctuation so that you no longer have a simple word in front of the =>:

my %x = ( X() => 'X' );
my %x = ( &X => 'X' );
cjm
+2  A: 

=> operator interprets its left side as a "string", the way qw() does.

Try using

my %x = ( X, 'X');
Arkadiy
+2  A: 

One way is to encapsulate X as (X):

my %x ( (X) => 1 );

Another option is to do away with '=>' and use ',' instead:

my %x ( X, 1 );
Frosty
+4  A: 

Use $hash{CONSTANT()} or $hash{+CONSTANT} to prevent the bareword quoting mechanism from kicking in.

From: http://perldoc.perl.org/constant.html

Chris
+14  A: 

Another option is to not use the use constant pragma and flip to Readonly as per recommendations in the Perl Best Practices by Damian Conway.

I switched a while back after realizing that constant hash ref's are just a constant reference to the hash, but don't do anything about the data inside the hash.

The readonly syntax creates "normal looking" variables, but will actually enforce the constantness or readonlyness. You can use it just like you would any other variable as a key.


use Readonly;

Readonly my $CONSTANT => 'Some value';

$hash{$CONSTANT} = 1;

shelfoo
A: 

Now, that was very very quick. Maybe, I should have googled a bit but S-O is making me a little bit lazier.

Thanks a lot. I would love to accept all answers but there can only be one correct answer and I will accept nohat's answer (as it was the first).

Jagmal
+3  A: 

The use constant pragma creates a subroutine prototyped to take no arguments. While it looks like a C-style constant, it's really a subroutine that returns a constant value.

The => (fat comma) automatically quotes left operand if its a bareword, as does the $hash{key} notation.

If your use of the constant name looks like a bareword, the quoting mechanisms will kick in and you'll get its name as the key instead of its value. To prevent this, change the usage so that it's not a bareword. For example:

use constant X => 1;
%hash = (X() => 1);
%hash = (+X => 1);
$hash{X()} = 1;
$hash{+X} = 1;

In initializers, you could also use the plain comma instead:

%hash = (X, 1);
Michael Carman
+5  A: 

Most of the other folks have answered your questioned well. Taken together, these create a very full explanation of the problem and recommended workarounds. The problem is that the Perl pragma "use constant" really creates a subroutine in your current package whose name is the the first argument of the pragma and whose value is the last.

In Perl, once a subroutine is declared, it may be called without parens.

Understanding that "constants" are simply subroutines, you can see why they are not interpolated in strings and why the "fat comma" operator "=>" which quotes the left-hand argument thinks you've handed it a string (try other built-in functions like time() and keys() sometime with the fat comma for extra fun).

Luckily, you may invoke the constant using explicit punctuation like parens or the ampersand sigil.

However, I've got a question for you: why are you using constants for hash keys at all?

I can think of a few scenarios that might lead you in this direction:

  1. You want control over which keys can be in the hash.

  2. You want to abstract the name of the keys in case these change later

In the case of number 1, constants probably won't save your hash. Instead, consider creating an Class that has public setters and getters that populate a hash visible only to the object. This is a very un-Perl like solution, but very easily to do.

In the case of number 2, I'd still advocate strongly for a Class. If access to the hash is regulated through a well-defined interface, only the implementer of the class is responsible for getting the hash key names right. In which case, I wouldn't suggest using constants at all.

Hope this helps and thanks for your time.

jjohn
+1  A: 

Comment @shelfoo (reputation not high enough to add comment directly there yet!)

Totally agree about Perl Best Practices by Damian Conway... its highly recommended reading.

However please read PBP Module Recommendation Commentary which is a useful "errata" if you plan to use PBP for an in-house style guide.

/I3az/

draegtun
Thanks for that, good to know. I've moved on from my perl gig, so I likely won't be using it other than at home, but I forwarded that link to one of the people I used to work with.
shelfoo