perl change ref of hash

2019-02-27 20:03发布

I ran into this and couldn't find the answer. I am trying to see if it is possible to "change" the reference of a hash. In other words, I have a hash, and a function that returns a hashref, and I want to make my hash point to the location in memory specified by this ref, instead of copying the contents of the hash it points to. The code looks something like this:

%hash = $h->hashref;

My obvious guess was that it should look like this:

\%hash = $h->hashref;

but that gives the error:

Can't modify reference constructor in scalar assignment

I tried a few other things, but nothing worked. Is what I am attempting actually possible?

标签: perl hash
3条回答
时光不老,我们不散
2楼-- · 2019-02-27 20:36

Sounds like you want:

use Data::Alias;
alias %hash = $h->hashref;

or if %hash is a package variable, you can instead just do:

*hash = $h->hashref;

But either way, this should almost always be avoided; why don't you simply use the hash reference?

查看更多
不美不萌又怎样
3楼-- · 2019-02-27 20:46

An experimental feature which would seemingly allow you to do exactly what you're describing has been added to Perl 5.21.5, which is a development release (see "Aliasing via reference").

查看更多
Lonely孤独者°
4楼-- · 2019-02-27 20:48

Yes, but…

References in perl are Scalars. What you are trying to achieve is aliasing the return value. This actually is possible, but you should not do this, since it involves messing with the symbol table. Furthermore this only works for globals (declared with our): If you assign a hashref to the glob *hash it will assign to the symbol table entry %hash:

#!/usr/bin/env perl

use warnings;
use strict;

sub a_hashref{{a => "one", b => "two"}}

our %hash;
*hash = a_hashref;

printf "%3s -> %s\n",$_,$hash{$_} foreach keys %hash;

This is bad style! It isn't in PBP (directly, but consider section 5.1: “non-lexicals shoudl be avoided”) and wont be reported by perlcritic but you shouldn't pollute the package namespace for a little syntactic fanciness. Furthermore it doesn't work with lexical variables (which is what you might want to use most of the time, because they are lexically scoped, not package wide). Another problem is, that if the $h->hashref method changes its return type, you'll suddenly assign to another table entry! (So if $h->hashref changes its return type to an arrayref, you assign to @hash, good luck detecting that). You could circumvent that by checking if $h->hashref really returns a hashref with 'HASH' eq ref$h->hashref` but that would defeat the purpose.

What is the problem with just keeping the reference? If you get a reference, why don't you just store it in a scalar?

$hash = $h->hashref

To read more about the global symbol table take a look at perlmod and consider perlref for the *FOO{THING} syntax, which sadly isn't for lvalues.

To achieve what you want, you could check out the several aliasing modules on cpan. Data::Alias or Lexical::Alias seem to fit your purpose. Also if you are interested in tie semantics and/or don't want to use XS modules, Tie::Alias might be worth a shoot.

查看更多
登录 后发表回答