In Perl, if a variable holds the name for another variable, how do I use the first variable to visit the other one?
For example, let
$name = "bob";
#bob = ("jerk", "perlfan");
how should I use $name to get to know what kind of person bob is?
Although I'm not quite sure, my vague memory tells me it might related to typeglob.
Several points:
You aren't talking about typeglobs, you are talking about symbolic references.
Don't use symbolic references -- they lead to hard to track down bugs.
In almost any case where symbolic references seem like a good idea, using a data structure based on a hash is the best approach.
Consider using Hash::Util to lock your hashes when you don't want to alter them.
Symbolic references don't work on lexical variables.
Typeglobs don't work on lexical variables.
Use lexical variables.
Don't use symbolic references.
See perlreftut for more on references (symbolic and otherwise).
See perldsc for help using data structures.
See perlmod for more on typeglobs.
See perlsub for more on lexical variables.
Here's an example of using locked hashes to control access to data based on the content of a variable:
use strict;
use warnings;
use Hash::Util qw( lock_hash unlock_hash );
my %data;
lock_hash( %data );
#Altering %data is a fatal exception.
unlock_hash( %data );
%data = (
'bob' => [ 'jerk', 'genius' ],
);
lock_hash( %data );
for my $name (qw/ bob margaret /) {
my $info = $data{$name}; # Fatal error when accessing data for margaret.
print "$name is a\n";
print map "\t$_\n", #$info;
}
All warnings aside, the syntax to use symbolic references should you need to use it (but you won't) is:
use strict;
use warnings;
my $name = 'bob';
our #bob = qw/jerk genius/;
my $qualities;
{ no strict 'refs';
print "$name: ", #$name, "\n";
$qualities = \#$name;
}
print "$name is a #$qualities\n";
Note that the array #bob is declared with our. Symbolic references only work with values in the symbol table. In other words, lexical variables do not work with symbolic references.
Just in case I haven't emphasized this enough, don't use symbolic references.
This is very bad practice. If you ever need to do it, it means you should refactor your code. Perhaps a hash with named keys would be better:
my %data = (
'bob' => [ 'jerk', 'perlfan' ],
);
my $name = 'bob';
print $data{ $name }->[0], "\n";
print $data{ $name }->[1], "\n";
my $stringified = join ' and ', #{ $data{ $name } };
print $stringified, "\n";
See perldoc -q "variable name":
Beginners often think they want to have a variable contain the name of a variable. ...
Short answer: Don't. Long answer: Read the FAQ entry (which is also available on your computer). Longer answer: Read MJD's Why it's stupid to "use a variable as a variable name" (Part 2, Part 3).
You really shouldn't have to do this, see depesz's answer for the alternative, but...
{
no strict 'refs';
print join(q{, }, #$name);
}
Yes, you can use a string scalar as a variable reference, and it looks up the variable by name.
You can also do this with typeglobs, see the Symbol module for an easy way.
Usually when you think you need to use symbolic references, you will be better served by using a hash (associative array).
use strict;
use warnings;
my %user = (
'bob' => [qw' jerk perlfan '],
'mary' => 'had a little lamb',
);
{
for my $name (qw'bob mary susan'){
if( exists $user{$name} ){
if( ref($user{$name}) eq 'ARRAY' ){
print join( ' ', #{$user{$name}} ), "\n";
}else{
print $name, ' ', $user{$name}, "\n";
}
}else{
print "$name not found\n";
}
}
}
Results in:
jerk perlfan
mary had a little lamb
susan not found
If you think you really need to use symbolic references, this is a safer way to do that.
use strict;
use warnings;
# can't use my
our #bob = ("jerk", "perlfan");
our $mary = 'had a little lamb';
{
for my $name (qw'bob mary susan'){
if( exists $::{$name} ){
my $ref = $::{$name};
if( *{$ref}{ARRAY} ){
print join(' ',#$ref), "\n";
}elsif( *{$ref}{SCALAR} ){
print "# $name is not an array, it's a scalar\n";
print $name, ' ', $$ref, "\n";
}
}else{
print "$name not found\n"
}
}
}
Which returns:
jerk perlfan
# mary is not an array, it's a scalar
mary had a little lamb
susan not found
You should notice that it is harder to symbolic references safely, which is why you shouldn't use them.
Unless of course you are an expert doing something advanced.
no strict 'refs';
print "$name is #$name\n";
Symbolic references are worth avoiding, but if you really want to use them, they have the same use syntax as regular references: see http://perlmonks.org/?node=References+quick+reference.
Related
Basically i am trying to access the predefined variable in a perl program.
the variables are in the form a1 a2 a3 format.
I want to access them in a loop. In the loop I will increment postfix scalar value
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
WHAT I EXPECT:
When I print $var in loop, I need the values 10,12 .. defined earlier.
WHAT I CAN NOT DO:
I am aware that such situation can be handled with a hash. But I do not have any control over the variable naming, hence I can not use hash or change variable format.
I appreciate your help!
If you want to avoid turning off strict, you could use eval:
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
print eval "\$a$i";
}
Update: using more readable version suggested by Сухой27 in the comments
Use an array instead of multiple similarly named variables, as this is their main use case,
use strict;
use warnings;
my #a = (10,12,14,16);
for my $i (0 .. $#a) {
my $var = $a[$i];
print $var, "\n";
}
alternatively you can use array of scalar references
use strict;
use warnings;
my ($a0,$a1,$a2,$a3) = (10,12,14,16);
my #a = \($a0,$a1,$a2,$a3);
for my $i (0 .. $#a) {
my $var = ${ $a[$i] };
print $var, "\n";
}
What you are doing here is called a symbolic reference, and it is an EXTREMELY bad idea.
Please take a look through this article: http://perl.plover.com/varvarname.html
But the long and short of it is - using a variable as a variable name - which you're doing - is dangerous and unnecessary. It causes all sorts of potential problems in your code, including bugs in completely unrelated pieces of code. This is why strict won't let you do it.
More importantly - it's completely unnecessary, because perl has the hash as a native data type.
Instead of your code, consider instead:
my %h;
( $h{0}, $h{1}, $h{2}, $h{3} ) = ( 10, 12, 14, 16 );
foreach my $key ( sort keys %h ) {
print "$key = $h{$key}\n";
}
Now, it's added a few characters to your code, but by doing so - you've created a lexically scoped namespace called %h. (I'd suggest calling it something more meaningful, personally - and definitely avoid $a and $b because they have special meanings).
But there is no danger of this namespace trampling over other parts of your code, and for bonus points - you no longer need your 'for' loop, you can simply iterate on keys instead. (So you always have the right number).
(Or as another user has suggested - just use an array)
You can get round strict's restriction on dynamic variable names like this.
#!/usr/bin/perl
use strict;
use warnings;
{
no strict 'refs';
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
}
I don't think this is a good idea, though!
I'm learning perl references, I understand the usefullness of the references to hases or arrays. But thinking about in what scenarios can be useful a reference to an scalar value.
my $x = 1;
my $sr = \$x;
where can be useful to use $$sr, instead of direct use of $x?
For example, when traversing any deep hashref structure, isn't the common practice returning a reference if the given node is hashref or arrayref, but returning directly the scalar value instead of returning a reference to the scalar?
Exists some functions or modules what uses or return references to scalars, instead of returning the scalar's value?
In a subroutine, when you want to directly modify the values being passed into it.
e.g.
sub increment_this {
my ( $inc_ref ) = #_;
${$inc_ref}++;
}
Bit of a trivial case I know, but perhaps more pertinent would be examples like chomp which removes trailing letters from a variable or $_.
If you think about it though - $_ is often effectively a reference.
If you do this:
my #array = qw ( 1 2 3 );
foreach ( #array ) {
$_++;
}
print #array;
Modifying $_ has modified the contents of the array.
I think the real answer lies in your question itself: References to scalars are not terribly useful, and you can probably just ignore that they exist when writing code. The main usefulness as hinted at by perreal is that you can point to the same memory location: Share a variable between objects.
However, for curiosity and academic purposes, one rather obscure thing comes to mind, besides the good examples left by perreal and Sobrique. You can open a file handle to print to a variable:
use strict;
use warnings;
use Data::Dumper;
my $string;
open my $fh, ">", \$string or die $!;
print $fh "Inside the string";
print Dumper $string;
# prints $VAR1 = 'Inside the string';
When you want to share a scalar between objects:
my $v = 1;
my %h1 = (a=>\$v);
my %h2 = (b=>\$v);
$v++;
print ${$h1{a}}, "\n";
print ${$h2{b}}, "\n";
prints:
2
2
It is usefull when you have to pass huge strings to a sub, and don't want to do a copy of them to make faster and don't waste the memory:
sub my_sub {
my $str = shift; #makes a copy of a reference => cheaper
#do something with $$str
}
#...
$x = ' ... large string ...';
my_sub(\$x);
#TLP mentioned file handles, which prompted another example:
Passing around file handles. Good practice for manipulating files is to use lexical file handles, to avoid namespace pollution.
sub write_header {
my ( $fh_ref ) = #_;
print ${$fh_ref} "This is a header\n";
}
open ( my $output, ">", "output_filename" );
write_header ( \$output );
write_content ( \$output );
write_footer ( \$output );
close ( $output );
I want to use input from the user to pick a hash to operate on. Currently, I have this cumbersome code:
my ( %hash1, %hash2 );
print "Which hash to work with (1 or 2)? ";
my $which = <>;
chomp $which;
&do_something( %hash1 ) if $which == 1;
&do_something( %hash2 ) if $which == 2;
Is there a more elegant way to do this? I was thinking of using %{"hash$which"}, but that doesn't seem to work. I think
$which = "hash" . chomp $which;
&do_something( %{"$which"} );
would work, but is that the best way to do it?
Thanks for the help, especially since this is my first SO post.
Why it's stupid to use a variable as a variable name
You want
my #hashes;
print "Which hash to work with (1 or 2)? ";
my $which = <>;
chomp $which;
do_something( %{ $hashes[$which] } );
Just in case you don't know, always use use strict; use warnings;.
What you are looking for is called a "symbolic reference" in the perl world. The use of symbolic references is strongly discouraged.
Why not add another level of indirection?
my $hash = {
user_opt1 => {
foo => 'bar',
},
user_opt2 => {
foo => 'baz',
},
};
In Perl, I'm learning how to dereference 'subroutine references'. But I can't seem to use a subroutine reference as a hash 'key'.
In the following sample code,
I can create a reference to a subroutine ($subref) and then dereference it to run the subroutine (&$subref)
I can use the reference as a hash 'value' and then easily dereference that
But I cannot figure out how to use the reference as a hash 'key'. When I pull the key out of the hash, Perl interprets the key as a string value (not a reference) - which I now understand (thanks to this site!). So I've tried Hash::MultiKey, but that seems to turn it into an array reference. I want to treat it as a subroutine/code reference, assuming this is somehow possible?
Any other ideas?
use strict;
#use diagnostics;
use Hash::MultiKey;
my $subref = \&hello;
#1:
&$subref('bob','sue'); #okay
#2:
my %hash;
$hash{'sayhi'}=$subref;
&{$hash{'sayhi'}}('bob','sue'); #okay
#3:
my %hash2;
tie %hash2, 'Hash::MultiKey';
$hash2{$subref}=1;
foreach my $key (keys %hash2) {
print "Ref type is: ". ref($key)."\n";
&{$key}('bob','sue'); # Not okay
}
sub hello {
my $name=shift;
my $name2=shift;
print "hello $name and $name2\n";
}
This is what is returned:
hello bob and sue
hello bob and sue
Ref type is: ARRAY
Not a CODE reference at d:\temp\test.pl line 21.
That is correct, a normal hash key is only a string. Things that are not strings get coerced to their string representation.
my $coderef = sub { my ($name, $name2) = #_; say "hello $name and $name2"; };
my %hash2 = ( $coderef => 1, );
print keys %hash2; # 'CODE(0x8d2280)'
Tieing is the usual means to modify that behaviour, but Hash::MultiKey does not help you, it has a different purpose: as the name says, you may have multiple keys, but again only simple strings:
use Hash::MultiKey qw();
tie my %hash2, 'Hash::MultiKey';
$hash2{ [$coderef] } = 1;
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key);
say 'Ref of the list elements produced by array-dereferencing the key are:';
say ref($_) for #{ $key }; # no output, i.e. simple strings
say 'List elements produced by array-dereferencing the key are:';
say $_ for #{ $key }; # 'CODE(0x8d27f0)'
}
Instead, use Tie::RefHash. (Code critique: prefer this syntax with the -> arrow for dereferencing a coderef.)
use 5.010;
use strict;
use warnings FATAL => 'all';
use Tie::RefHash qw();
my $coderef = sub {
my ($name, $name2) = #_;
say "hello $name and $name2";
};
$coderef->(qw(bob sue));
my %hash = (sayhi => $coderef);
$hash{sayhi}->(qw(bob sue));
tie my %hash2, 'Tie::RefHash';
%hash2 = ($coderef => 1);
foreach my $key (keys %hash2) {
say 'Ref of the key is: ' . ref($key); # 'CODE'
$key->(qw(bob sue));
}
From perlfaq4:
How can I use a reference as a hash key?
(contributed by brian d foy and Ben Morrow)
Hash keys are strings, so you can't really use a reference as the key.
When you try to do that, perl turns the reference into its stringified
form (for instance, HASH(0xDEADBEEF) ). From there you can't get back
the reference from the stringified form, at least without doing some
extra work on your own.
Remember that the entry in the hash will still be there even if the
referenced variable goes out of scope, and that it is entirely
possible for Perl to subsequently allocate a different variable at the
same address. This will mean a new variable might accidentally be
associated with the value for an old.
If you have Perl 5.10 or later, and you just want to store a value
against the reference for lookup later, you can use the core
Hash::Util::Fieldhash module. This will also handle renaming the keys
if you use multiple threads (which causes all variables to be
reallocated at new addresses, changing their stringification), and
garbage-collecting the entries when the referenced variable goes out
of scope.
If you actually need to be able to get a real reference back from each
hash entry, you can use the Tie::RefHash module, which does the
required work for you.
So it looks like Tie::RefHash will do what you want. But to be honest, I don't think that what you want to do is a particularly good idea.
Why do you need it? If you e.g. need to store parameters to the functions in the hash, you can use HoH:
my %hash;
$hash{$subref} = { sub => $subref,
arg => [qw/bob sue/],
};
foreach my $key (keys %hash) {
print "$key: ref type is: " . ref($key) . "\n";
$hash{$key}{sub}->( #{ $hash{$key}{arg} } );
}
But then, you can probably choose a better key anyway.
I'm stuck on this problem for days and don't know which data structure I should use in Perl
Suppose I have this file with the following inputs:
lookup hello
lookup good night
good night
I want to be able to store "lookup" linked with "hello", "lookup" linked with "good night" and lastly "good night" linked with nothing else. Should I use a hash or an array? If I set "lookup" as the key of the hash, I will lose "hello" information. If I set "good night" as key, I will end up losing the 3rd line of information.
What should I do?
EDIT: There are certain keywords (which I called "lookup") that has stuff that are linked to them. For example, "password" will be followed by the actual password whereas "login" need not have anything following it.
It's not exactly clear how you're expecting to break up the words here, but in the general case, if you need to do random lookups (by arbitrary word), a hash is a better fit than an array. Without additional details on exactly what you're trying to do here, it's hard to be more specific.
It looks like you want a hash of arrays. Ignoring the issue of how to decide the key is "good night" instead of "good" (and just assuming the key should be "good"), you could do something like:
#!/usr/bin/env perl
use strict;
use warnings;
my %hoa; # hash of arrays
while(<>) {
my #f = split;
my $k = shift #f;
$hoa{ $k } = [] unless $hoa{ $k };
push #{$hoa{ $k }}, join( ' ', #f );
}
foreach my $k( keys( %hoa )) {
print "$k: $_\n" foreach ( #{$hoa{ $k }});
}
Not quite sure what you want, but, is this ok:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dump qw(dump);
# build a hash of keywords
my %kword = map{ $_ => 1}qw(lookup kword1 kword2);
my %result;
while(<DATA>) {
chomp;
my ($k, $v) = split(/ /,$_, 2);
push #{$result{$k}}, $v if exists $kword{$k};
}
dump%result;
__DATA__
lookup hello
lookup good night
good night
kword1 foo
kword1 bar
output:
("kword1", ["foo", "bar"], "lookup", ["hello", "good night"])
You could also use a hash of hashes if you plan to store another piece of data with your linked "lookup"->"good night" which will be easy to access later on.
E.g:
%result = (
'lookup' => {
'hello' => $storage1,
'good night' => $storage2,
},
'good night' => {
}
);