perl: call hash values as methods - perl

I have an usual hash or hashref.
my %hash; $hash{'key'} = 'value';
not very easy all this quote marks and curly brackets
I know there is a trick to call hash values like methods:
$hash->key = 'value'; # even no need for round brackets !
May be to use some magick module, I know it exist, I have seen this code once:
use CallHashLikeMethods 'hash';
$hash->key = 'value';
Of course, I can write the class for this hash and then TIE it, but it is very manual;
I looking for a magic moduule which prepares hash automatically. I just forget it's name

What you are asking to do is a fairly bad idea:
Maintainability: When a hash access doesn't look like a hash access, that's bad.
Performance: Method calls are much more expensive than hash accesses.
Correctness: An overdose of cleverness could make other clever code break. Keep your code simple and stupid.
Furthermore, this will not save you any typing, because the keys in a hash access are auto-quoted:
$hash{key} = 'value';
$hash->key = 'value';
… as long as the key is a valid bareword.

I don't know of any pre-written CPAN modules that do this, but it's not exactly difficult...
use v5.10;
use strict;
use warnings;
sub HASH::AUTOLOAD :lvalue {
my ($key) = ($HASH::AUTOLOAD =~ /(\w+)\z/);
shift->{$key};
}
my $hash = {
foo => 1,
bar => 0, # not 2
baz => 3,
};
bless($hash, 'HASH');
$hash->bar = 1;
$hash->bar++;
say $hash->foo;
say $hash->bar;
say $hash->baz;
I agree with amon though - normal hash access will be clearer and faster, and the syntax is not exactly onerous.
Update: found a CPAN module for it: Hash::AsObject.

If you want to use a fixed set of keys as structure values, you might like Struct::Dumb
use Struct::Dumb;
struct Point => ['x', 'y', 'z'];
my $p = Point(10, 20, 30);
$p->x = 40;

Related

How to make a particular change in all the keys of a hash?

I have a hash which is like this:
'IRQ_VSAFE_LPM_ASC_0' => '140',
'IRQ_VSAFE_LPM_ASC_1' => '141'.......and so on
I want to replace ASC_ by ASC_1 in all keys in the hash. I tried this:
foreach $_(keys $hash)
{
s/ASC_/ASC_1/g;
}
but it's not working.
You have to delete old keys from the hash and insert new ones,
use strict;
use warnings;
sub rename_keys {
my ($hash, $func) = #_;
my #k1 = my #k2 = keys %$hash;
$func->() for #k2;
#$hash{#k2} = delete #$hash{#k1};
}
my %hash = (
'IRQ_VSAFE_LPM_ASC_0' => '140',
'IRQ_VSAFE_LPM_ASC_1' => '141',
);
rename_keys(\%hash, sub { s/ASC_/ASC_1/ });
The previous answer addressed a way to do what you want. However, it also makes sense to explain why what you tried to do didn't work.
The problem is that the syntax used for working with hashes in Perl can mislead you with its simplicity compared to the actual way the hash works underneath.
What you see in Perl code is simply two pieces of information: a hash key and a corresponding hash value: $myHash{$key} = $value; or even more misleading %myHash = ($key => $value);
However, the way the hashes work, this isn't merely storing the key and a value as a pair, as the code above may lead you into thinking. Instead, a hash is a complicated data structure, in which the key serves as an input into the addressing which is done via a formula (hash function) and an algorithm (to deal with collistions) - the details are well covered on Wikipedia article.
As such, changing a hash key as if it was merely a value isn't enough, because what is stored in the hash isn't just a value - it's a whole data structure with addressing based on that value. Therefore when you change a hash key, it would ALSO change the location of the value in the data structure, and doing that isn't possible without removing the old entry and adding a brand new entry under a new key, which will delete and re-insert the value in the correct place.
A simple way to do this may be to use pairmap from recent List::Util.
use 5.014; # so we can use the /r flag to s///
use List::Util qw( pairmap );
my %new = pairmap { ($a =~ s/ASC_/ASC_1/r) => $b } %oldhash;

In Perl, how can I use the contents of a variable as the name of a hash? [duplicate]

This question already has answers here:
How can I use a variable as a variable name in Perl?
(3 answers)
Closed 6 years ago.
The code below works only with no strict.
Can anyone suggest a better way to do it?
%hash5=('key5', 5);
my $hash_name = 'hash5';
print $$hash_name{'key5'}, "\n";
Again my aim: I do not know the hash name. I only know, it is store in
the variable $hash_name. People have been suggesting things like:
my $hashref = \%hashABC;
which requires me to know that the hash name is '%hashABC'.
Using this example just above i would like to do sth like:
my $hash_name = 'hashABC';
my $hashref = \%$hash_name; # not possible, hope u get the aim
now i do not need to know the name of the hash anymore.
That is what i want.
Thx a lot guys!
(perl 5)
Instead of referring to the hash by name, use references.
# Here is our hash
my %hash = (key => 5);
# we make a reference to the hash
# this is like remembering the name of the variable, but safe
my $hashref = \%hash;
# here are two ways to access values in the referenced hash
say $$hashref{key};
say $hashref->{key}; # prefer this
Alternatively, keep a hash of hashes so that you can look up items by name:
# here is our hash again
my %hash = (key => 5);
# and here is a hash that maps names to hash references
my %hash_by_name;
# we remember the %hash as "hash5"
$hash_by_name{hash5} = \%hash;
# now we can access an element in that hash
say $hash_by_name{hash5}{key};
# we can also have a variable with the name:
my $name = "hash5";
say $hash_by_name{$name}{key};
Learn more about references in perlreftut.
In this case, disable strict temporally looks like the best solution, you can do it like this
#!/usr/bin/perl
use strict;
use warnings;
our %hash5=('key5', 5);
my $hash_name = 'hash5';
my $hash_ref;
{
no strict "refs";
$hash_ref = \%$hash_name;
}
print $hash_ref->{key5}, "\n";
Note: For this to work, %hash5 must be a global variable.
I don't know where the data in %hash_name come from. Did you read and store them in %hash_name? If so, perhaps a simpler solution would be to modify your program to read into a hash of hashes (as many have suggested), instead of reading into a global variable:
my %data = (
hash_name => get_data(),
);
# and then ...
my $name = get_name(); # e.g., 'hash_name'
my $data = $data{ $name } or do {
# error handling
...
};
Remember that the limitations that use strict imposes do not apply at all to the keys of a hash :-)

Perl: How to break on write-to-variable in Eclipse

I have a script that is writing entries into a hash. However, at a certain point entries exist in the hash that I think should not. So, obviously I've cocked it up somewhere, but there is only one place where I think I add elements in to the hash and I've tested that to make sure that these "rogue" elements aren't being added at this location.
What I would like to do is break on a write to the hash, something like this, but in a "global" kinda way because I don't know where this stray write is in the code - I can't see it...
So what are my options? Can I set a watch point in the EPIC debugger and if so how? (I've had a play but can;t find anything relevant).
Or could I perhaps create a extended hash that can intercept writes somehow?
Any ideas on an "easy" debugging method. Otherwise I think I'll be back to brute force debug :S Thanks in davance...
Not an EPIC-specific answer, but check out Tie::Watch. You can setup a variable (like a hash) to be watched, and your program can output something every time the variable is updated.
updated: Tie::Trace does pretty much the same thing, with a simpler interface.
Here is a DIY-version of mobs answer: Make that hash a tied hash to a class that outputs a stack trace on every write access:
package MyHash {
use Tie::Hash;
use parent -norequire, "Tie::StdHash";
use Carp "cluck";
sub STORE {
my ($self, $k, $v) = #_;
cluck "hash write to $self: $k => $v";
$self->SUPER::STORE($k => $v);
}
}
tie my %hash => "MyHash";
$hash{foo} = 42;
print "$hash{foo}\n";
Output:
hash write to MyHash=HASH(0x91be87c): foo => 42 at -e line 1.
MyHash::STORE('MyHash=HASH(0x91be87c)', 'foo', 42) called at -e line 1
42
use Hash::Util qw(
lock_keys unlock_keys
lock_hash unlock_hash
);
my %hash = (foo => 42, bar => 23);
# no keys change
lock_keys(%hash);
# no keys/values change
lock_hash(%hash);

Does Perl have an associative array type that can have any type of keys?

It seems that Perl can only have strings as the keys of a hash. (Has this been changed in a certain version of Perl?) It is very limiting to only be able to use strings as the key. What if I wanted an object or an array to be the key? In Python it is easy to use an array, tuple, and other objects that can compare, as dict keys. Perl does have the ability to compare things like arrays for equality, so I don't see why they can't be used as mapping-type keys.
Isn't there a way to use any key type in Perl? Is there a module that provides this?
Contrary to what you said, Perl does NOT have the ability to compare things like arrays for equality as you claim. For starters, Perl has no definition for array equality. And if the definition requires comparing the contents of the array, then Perl doesn't have definitions of equality for most things that can be found in an array either.
The closest Perl has for a definition of equality arrays is their address. If that's what you want to use, then it's quite easy:
$key = ['a', 'b'];
$hash{$key} = [ $key, $val ]; # Prevents the key from being freed.
print $hash{$key}[1];
Otherwise, Perl leaves it up to you to implement what you want instead of forcing you to use what it provides. I see two main approaches.
A tied hash, basically code that presents the interface of a hash without actually being a hash table, can support any key type. You could use it to define your version of array equality. There might even be an existing module (although I didn't see one after a very quick search).
Another approach would be to create a function that produces a unique key from the key expression.
sub key{ "#_" } # Overly simplistic?
$hash{key('a', 'b')} = $val;
print $hash{key('a', 'b')};
AFAIK Perl simply stringifies the keys?
package Foo;
use Moose;
use overload ('""' => \&stringify);
sub stringify {
return "xxx";
}
package main;
use v5.10;
use Data::Dump 'pp';
my $foo = Foo->new;
my $hash = {$foo => 'bar'};
say pp($hash); // { xxx => "bar" }
This way you can also use whatever you want as a hash key. See also this thread on Perl Monks.
As for the equality, take a look at equality operators in perlop. The == operator compares numerically, the eq operator compares stringwise. This means that for example (4, 2) == (1, 2) is true (as scalar (4, 2) is 2), which might be a surprise for you.
Yes, there is module for this on CPAN: Tie::Hash::StructKeyed. It takes into account whole structure, but as a snapshot. each/keys will return original reference.
Yes it does, use fieldhashes (Hash::Util::FieldHash for 5.10+ and Hash::Util::FieldHash::Compat for before 5.10) to register your hashes as fieldhashes, and you can use any reference (and thus, any object) as a key for those hashes (it numifies the reference, essentially, and provides logic for dealing with CLONEing across threads and garbage collecting the keys), for example:
use Hash::Util qw/fieldhashes/;
fieldhashes \my (%foo, %bar, %baz);
my %data = (a => 1, b => 2, c => 3);
my $stuff = {t => 2, u => 3, l => 9};
$foo{\%data} = 'x';
$foo{$stuff} = 'a';
$bar{\%data} = 'y';
$bar{$stuff} = 'b';
$baz{\%data} = 'z';
$baz{$stuff} = 'c';
print "$foo{\%data} $bar{\%data} $baz{\%data}\n";
print "$foo{$stuff} $bar{$stuff} $baz{$stuff}\n";

Confusion about proper usage of dereference in Perl

I noticed the other day that - while altering values in a hash - that when you dereference a hash in Perl, you actually are making a copy of that hash. To confirm I wrote this quick little script:
#! perl
use warnings;
use strict;
my %h = ();
my $hRef = \%h;
my %h2 = %{$hRef};
my $h2Ref = \%h2;
if($hRef eq $h2Ref) {
print "\n\tThey're the same $hRef $h2Ref";
}
else {
print "\n\tThey're NOT the same $hRef $h2Ref";
}
print "\n\n";
The output:
They're NOT the same HASH(0x10ff6848) HASH(0x10fede18)
This leads me to realize that there could be spots in some of my scripts where they aren't behaving as expected. Why is it even like this in the first place? If you're passing or returning a hash, it would be more natural to assume that dereferencing the hash would allow me to alter the values of the hash being dereferenced. Instead I'm just making copies all over the place without any real need/reason to beyond making syntax a little more obvious.
I realize the fact that I hadn't even noticed this until now shows its probably not that big of a deal (in terms of the need to go fix in all of my scripts - but important going forward). I think its going to be pretty rare to see noticeable performance differences out of this, but that doesn't alter the fact that I'm still confused.
Is this by design in perl? Is there some explicit reason I don't know about for this; or is this just known and you - as the programmer - expected to know and write scripts accordingly?
The problem is that you are making a copy of the hash to work with in this line:
my %h2 = %{$hRef};
And that is understandable, since many posts here on SO use that idiom to make a local name for a hash, without explaining that it is actually making a copy.
In Perl, a hash is a plural value, just like an array. This means that in list context (such as you get when assigning to a hash) the aggregate is taken apart into a list of its contents. This list of pairs is then assembled into a new hash as shown.
What you want to do is work with the reference directly.
for (keys %$hRef) {...}
for (values %$href) {...}
my $x = $href->{some_key};
# or
my $x = $$href{some_key};
$$href{new_key} = 'new_value';
When working with a normal hash, you have the sigil which is either a % when talking about the entire hash, a $ when talking about a single element, and # when talking about a slice. Each of these sigils is then followed by an identifier.
%hash # whole hash
$hash{key} # element
#hash{qw(a b)} # slice
To work with a reference named $href simply replace the string hash in the above code with $href. In other words, $href is the complete name of the identifier:
%$href # whole hash
$$href{key} # element
#$href{qw(a b)} # slice
Each of these could be written in a more verbose form as:
%{$href}
${$href}{key}
#{$href}{qw(a b)}
Which is again a substitution of the string '$href' for 'hash' as the name of the identifier.
%{hash}
${hash}{key}
#{hash}{qw(a b)}
You can also use a dereferencing arrow when working with an element:
$hash->{key} # exactly the same as $$hash{key}
But I prefer the doubled sigil syntax since it is similar to the whole aggregate and slice syntax, as well as the normal non-reference syntax.
So to sum up, any time you write something like this:
my #array = #$array_ref;
my %hash = %$hash_ref;
You will be making a copy of the first level of each aggregate. When using the dereferencing syntax directly, you will be working on the actual values, and not a copy.
If you want a REAL local name for a hash, but want to work on the same hash, you can use the local keyword to create an alias.
sub some_sub {
my $hash_ref = shift;
our %hash; # declare a lexical name for the global %{__PACKAGE__::hash}
local *hash = \%$hash_ref;
# install the hash ref into the glob
# the `\%` bit ensures we have a hash ref
# use %hash here, all changes will be made to $hash_ref
} # local unwinds here, restoring the global to its previous value if any
That is the pure Perl way of aliasing. If you want to use a my variable to hold the alias, you can use the module Data::Alias
You are confusing the actions of dereferencing, which does not inherently create a copy, and using a hash in list context and assigning that list, which does. $hashref->{'a'} is a dereference, but most certainly does affect the original hash. This is true for $#$arrayref or values(%$hashref) also.
Without the assignment, just the list context %$hashref is a mixed beast; the resulting list contains copies of the hash keys but aliases to the actual hash values. You can see this in action:
$ perl -wle'$x={"a".."f"}; for (%$x) { $_=chr(ord($_)+10) }; print %$x'
epcnal
vs.
$ perl -wle'$x={"a".."f"}; %y=%$x; for (%y) { $_=chr(ord($_)+10) }; print %$x; print %y'
efcdab
epcnal
but %$hashref isn't acting any differently than %hash here.
No, dereferencing does not create a copy of the referent. It's my that creates a new variable.
$ perl -E'
my %h1; my $h1 = \%h1;
my %h2; my $h2 = \%h2;
say $h1;
say $h2;
say $h1 == $h2 ?1:0;
'
HASH(0x83b62e0)
HASH(0x83b6340)
0
$ perl -E'
my %h;
my $h1 = \%h;
my $h2 = \%h;
say $h1;
say $h2;
say $h1 == $h2 ?1:0;
'
HASH(0x9eae2d8)
HASH(0x9eae2d8)
1
No, $#{$someArrayHashRef} does not create a new array.
If perl did what you suggest, then variables would get aliased very easily, which would be far more confusing. As it is, you can alias variables with globbing, but you need to do so explicitly.