Perl - Hash and the => operator - perl

So Im learning Perl and got to the chapter about hashes. I understand that the '=>' operator is an alias to the comma operator. But when I try to make a value undef I get the warning
Use of uninitialized value $last_name{"dino"} in concatenation (.) or string at ./learning.pl line 18.
use warnings;
use strict;
my %last_name = (
fred => 'flintston',
dino => undef,
barney => 'rubble',
betty => 'rubble',
);
my $key;
my $value;
if(%last_name) {
foreach $key (sort keys %last_name) {
print("$key => $last_name{$key}\n");
}
}
But when I change the hash line to:
my %last_name = (
fred => 'flintston',
dino => undef =>
barney => 'rubble',
betty => 'rubble',
);
It works just fine and returns the value as undef. All I did was replace the comma operator separating the key/value with the '=>' operator.
Why does the later work but not the former if the two operators are supposed to be the same?

They are not exactly the same. The "fat comma" does one more thing: it quotes the left hand side operand when it's a bareword. Therefore,
undef, 1
is equivalent to
undef(), 1
while
undef => 1
is equivalent to
'undef', 1
See perlop:
The => operator is a synonym for the comma except that it causes a word on its left to be interpreted as a string if it begins with a letter or underscore and is composed only of letters, digits and underscores.

Choroba has already answered, but I think it can be useful to expand on this a little.
In the first case, when you get a warning, it's because you are printing an undef value: the value of the key 'dino' is undefined. Note that when you set the values of the hash you don't get any warning, because giving an undefined value to a key is perfectly normal. The warning shows up only when you try to use this undefined value; it would be better to check it before you use it, like this:
if (defined $last_name{$key}) {
print("$key => $last_name{$key}\n");
}
This way you can avoid the warning.
The interesting part comes in your second case. To understand what happens, you can run this code at the end:
if (defined $last_name{'dino'}) {
print "last_name{'dino'} is defined, and its value is $last_name{'dino'}\n";
if ($last_name{'dino'} =~ /^[u][n][d][e][f]$/) {
print "It matches, which means that the value of last_name{'dino'} " .
"is not undefined, it's the string 'undef'\n";
}
}
and both if are true, which indicates that the value for $last_name{'dino'} is defined, and it's the string 'undef'. Of course it's confusing, but it is defined. Therefore, you don't get any warning.
All of this is a way to see what is happening. To understand why, please check choroba's answer.

Related

Unable to properly merge hashes

I currently have the following
# $dog, $cat, $rat are all hash refs
my %rethash = ('success' => 'Your Cool');
my %ref ={ 'dog' => $dog, 'cat' => $cat, 'mouse' => $rat,
'chicken' => '' };
my $perlobj = ( \%ref,\%rethash );
When $perlobj is dumped this is the result
$VAR1 = {
'success' => 'Your Cool'
};
However when warnings are enabled I get the following message
Useless use of reference constructor in void context at ..
I realize there is something terribly wrong with how %ref is assigned using {}, What is wrong with this code? I can't seem to get rid of this warning....
EDIT:
Ok I think I figured out whats going on,
my $perlobj = ( \%ref,\%rethash );
This does not merge but results in $perlobj becoming a reference to %rethash, this is obvious after reading your responses.
What RobEarl is saying is correct. I'll give an explanation of that and add some more stuff.
Your variable name %ref and the fact that you are using {} kinda implies you want a reference here.
Let's take a look what value we will have in %ref. Consider this example.
use strict; use warnings;
use Data::Printer;
my %foo = { key => 'value' };
p %foo;
This will throw a warning Reference found where even-sized list expected on my Perl 5.20.2. The output will be:
{
HASH(0x7e33c0) undef
}
It's a hash with a hashref as the key and undef as a value. HASH(0x07e33c0) is what you get when you look at a hash reference without dereferencing it. (The {} are there because Data::Printer converts the hash to a hashref).
Back to your code, the correct sigil for a reference is $. It does not matter what kind of reference it is. The reference is always a scalar (a pointer to the place in memory where the hash/array/something) is stored.
my $ref = {
dog => $dog,
cat => $cat,
mouse => $rat,
chicken => '', # maybe this should be undef?
};
Now you've got a hashref with the values of $dog, $cat, $rat and an empty string.
Now you're assigning a variable named $perlobj, which implies it's an object. Instead you are assigning a scalar variable (the $ makes it a scalar) with a list. If you do that, Perl will only assign the right-most value to the variable.
my $foo = (1, 2, 3); # $foo will be 3 and there's a warning
You are assigning a list of two references. The first one is disregarded and only \$rethash gets assigned. That works because conveniently, $perlobj is a scalar, and references are also scalars. So now $perlobj is a reference of %rethash. That's why your Data::Dumper output looks like %rethash.
I'm not sure what you want to do, so I cannot really help you with that. I suggest you read up on some stuff.
perlreftut is useful to learn how references work
If you want to do Object Oriented Programming, check out Moose
It might also be useful to just go get a book to learn a bit more about basic Perl. Learning Perl by Randal L. Schwartz and Beginning Perl by Curtis Poe are both very good for that
You are taking a list of hash references and assigning them to a scalar
my $perlobj = ( \%ref, \%rethash ); # same as $perlobj = \%rethash
Instead you want to take a reference to a merger of hashes
my $perlobj = { %ref, %rethash };

Why does Perl function "map" give the error "Not enough arguments for map"

Here is the thing I don't understand.
This script works correctly (notice the concatenation in the map functin):
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %aa = map { 'a' . '' => 1 } (1..3);
print Dumper \%aa;
__END__
output:
$VAR1 = {
'a' => 1
};
But without concatenation the map does not work. Here is the script I expect to work, but it does not:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
my %aa = map { 'a' => 1 } (1..3);
print Dumper \%aa;
__END__
output:
Not enough arguments for map at e.pl line 7, near "} ("
syntax error at e.pl line 7, near "} ("
Global symbol "%aa" requires explicit package name at e.pl line 9.
Execution of e.pl aborted due to compilation errors.
Can you please explain such behaviour?
Perl uses heuristics to decide whether you're using:
map { STATEMENTS } LIST; # or
map EXPR, LIST;
Because although "{" is often the start of a block, it might also be the start of a hashref.
These heuristics don't look ahead very far in the token stream (IIRC two tokens).
You can force "{" to be interpreted as a block using:
map {; STATEMENTS } LIST; # the semicolon acts as a disambigator
You can force "{" to be interpreted as a hash using:
map +{ LIST }, LIST; # the plus sign acts as a disambigator
grep suffers similarly. (Technically so does do, in that a hashref can be given as an argument, which will then be stringified and treated as if it were a filename. That's just weird though.)
Per the Documentation for map:
Because Perl doesn't look ahead for the closing } it has to take a guess at which it's dealing with based on what it finds just after the {. Usually it gets it right, but if it doesn't it won't realize something is wrong until it gets to the }
Giving the examples:
%hash = map { "\L$_" => 1 } #array # perl guesses EXPR. wrong
%hash = map { +"\L$_" => 1 } #array # perl guesses BLOCK. right
So adding + will give you the same as the first example you've given
my %aa = map { +'a'=> 1 } (1..3);
Perl's manpage entry for map() explains this:
"{" starts both hash references and blocks, so "map { ..."
could be either the start of map BLOCK LIST or map EXPR, LIST.
Because Perl doesn't look ahead for the closing "}" it has to
take a guess at which it's dealing with based on what it finds
just after the "{". Usually it gets it right, but if it doesn't
it won't realize something is wrong until it gets to the "}"
and encounters the missing (or unexpected) comma. The syntax
error will be reported close to the "}", but you'll need to
change something near the "{" such as using a unary "+" to give
Perl some help:
%hash = map { "\L$_" => 1 } #array # perl guesses EXPR. wrong
%hash = map { +"\L$_" => 1 } #array # perl guesses BLOCK. right
%hash = map { ("\L$_" => 1) } #array # this also works
%hash = map { lc($_) => 1 } #array # as does this.
%hash = map +( lc($_) => 1 ), #array # this is EXPR and works!
%hash = map ( lc($_), 1 ), #array # evaluates to (1, #array)
or to force an anon hash constructor use "+{":
#hashes = map +{ lc($_) => 1 }, #array # EXPR, so needs comma at end
to get a list of anonymous hashes each with only one entry
apiece.
Based on this, to get rid of the concatenation kludge, you'd need to adjust your syntax to one of these instead:
my %aa = map { +'a' => 1 } (1..3);
my %aa = map { ('a' => 1) } (1..3);
my %aa = map +( 'a' => 1 ), (1..3);
The braces are a little ambiguous in the context of map. They can be surrounding a block as you are intending, or they can be an anonymous hash constructor. There is some fuzzy logic in the perl parser which tries to guess which one you mean.
Your second case looks more like an anonymous hash to perl.
See the perldoc for map which explains this and gives some workarounds.

Why does this map block contain an apparently useless +?

While browsing the source code I saw the following lines:
my #files_to_keep = qw (file1 file2);
my %keep = map { + $_ => 1 } #files_to_keep;
What does the + do in this code snippet? I used Data::Dumper to see whether taking out the plus sign does anything, but the results were the same:
$ perl cleanme.pl
$VAR1 = {
'file1' => 1,
'file2' => 1
};
This is used to prevent a parsing problem. The plus symbol forces the interpreter to behave like a normal block and not an expression.
The fear is that perhaps you are trying to create a hashreference using the other (expression) formulation of map like so.
#array_of_hashrefs = map { "\L$_" => 1 }, #array
Notice the comma. Then if the parser guesses that you are doing this given the statement in the OP there will a syntax error for missing the comma! To see the difference try quoting "$_". For whatever reason, the parser takes this as enough to trigger the expression behavior.
Yes its an oddity. Therefore many extra-paranoid Perl programmers toss in the extra plus sign more often than needed (me included).
Here are the examples from the map documentation.
%hash = map { "\L$_" => 1 } #array # perl guesses EXPR. wrong
%hash = map { +"\L$_" => 1 } #array # perl guesses BLOCK. right
%hash = map { ("\L$_" => 1) } #array # this also works
%hash = map { lc($_) => 1 } #array # as does this.
%hash = map +( lc($_) => 1 ), #array # this is EXPR and works!
%hash = map ( lc($_), 1 ), #array # evaluates to (1, #array)
For a fun read (stylistically) and a case where the parser gets it wrong read this: http://blogs.perl.org/users/tom_wyant/2012/01/the-case-of-the-overloaded-curlys.html
The unary-plus operator simply returns its operand unchanged. Adding one doesn't even change the context.
In the example you gave, it is completely useless. But there are situations where it is useful to make the next token something that's undeniably an operator.
For example, map has two syntaxes.
map EXPR, LIST
and
map BLOCK LIST
A block starts with {, but so can an expression. For example, { } can be a block or a hash constructor.
So how can map tell the difference? It guesses. Which means it's sometimes wrong.
One occasion where is guesses wrong is the following:
map { $_ => 1 }, #list
You can prod it in to guessing correctly using + or ;.
map {; ... # BLOCK
map +{ ... # EXPR
So in this case, you could use
map +{ foo => $_ }, #list
Note that you could also use the following:
map({ foo => $_ }, #list)
Another example is when you omit the parens around arguments, and the first argument expression starts with a paren.
print ($x+$y)*2; # Same as: 2 * print($x+$y)
It can be fixed using
print +($x+$y)*2;
But why pile on a hack just to avoid parens? I prefer
print(($x+$y)*2);

Hash Key and Value in Perl

I have the question in Perl:Read a series of last names and phone numbers from the given input. The names and numbers should be separated by a comma. Then print the names and numbers alphabetically according to last name. Use hashes.
#!usr/bin/perl
my %series = ('Ashok','4365654435' 'Ramnath','4356456546' 'Aniketh','4565467577');
while (($key, $value) = each(sort %series))
{
print $key.",".$value."\n";
}
I am not getting the output. Where am I going wrong? Please help. Thanks in advance
#!usr/bin/perl
my %series = ('Ashok','4365654435' 'Ramnath','4356456546' 'Aniketh','4565467577');
print $_.",".$series{$_}."\n" for sort keys %series;
If I execute any of the above 2 programs, I get the same output as:
String found where operator expected at line 2, near "'4365654435' 'Ramnath'" (Missing operator before 'Ramnath'?)
String found where operator expected at line 2, near "'4356456546' 'Aniketh'" (Missing operator before 'Aniketh'?)
syntax error at line 2, near "'4365654435' 'Ramnath'"
Execution aborted due to compilation errors
But according to the question, I think I cannot store the input as my %series = ('Ashok','4365654435','Ramnath','4356456546','Aniketh','4565467577');
each only operates on hashes. You can't use sort like that, it sorts lists not hashes.
Your loop could be:
foreach my $key (sort keys %series) {
print $key.",".$series{$key}."\n";
}
Or in shorthand:
print $_.",".$series{$_}."\n" for sort keys %series;
In your hash declaration you have:
my %series = ('Ashok','4365654435' 'Ramnath','4356456546' 'Aniketh','4565467577');
This is generating the warnings.
A hash is simply an even list of scalars. Therefore, you have to put a comma between each pair:
my %series = ('Ashok','4365654435', 'Ramnath','4356456546', 'Aniketh','4565467577');
# ^--- ^---
If you want visual distinction between the pairs, you can use the => operator. This behaves the same as the comma. Additionaly, if the left hand side is a legal bareword, it is viewed as a quoted string. Therefore, we could write any of these:
# it is just a comma after all, with autoquoting
my %series = (Ashok => 4365654435 => Ramnath => 4356456546 => Aniketh => 4565467577);
# using it as a visual "pair" constructor
my %series = ('Ashok'=>'4365654435', 'Ramnath'=>'4356456546', 'Aniketh'=>'4565467577');
# as above, but using autoquoting. Numbers don't have to be quoted.
my %series = (
Ashok => 4365654435,
Ramnath => 4356456546,
Aniketh => 4565467577,
);
This last solution is the best. The last coma is optional, but I consider it good style—it makes it easy to add another entry. You can use autoquoting whenever the bareword on the left would be a legal variable name. E.g. a_bc => 1 is valid, but a bc => 1 is not (whitespace is not allowed in variable names), and +/- => 1 is not allowed (reserved characters). However Ünıçøðé => 1 is allowed when your source code is encoded in UTF-8 and you use uft8 in your script.
Besides what amonand Mat said, I'd like to notice other issues in your code:
your shebang is wrong it should be #!/usr/bin/perl - notice the first /
you don't have use strict; and use warnings; in your code - although this is not strictly a mistake, I consider this to be an issue. Those 2 commands will save you from a lot of trouble later on.
PS: you have to use commas between your number and names also, not only between names and numbers - you have to, because otherwise you get a compile error

Is %$var dereferencing a Perl hash?

I'm sending a subroutine a hash, and fetching it with my($arg_ref) = #_;
But what exactly is %$arg_ref? Is %$ dereferencing the hash?
$arg_ref is a scalar since it uses the $ sigil. Presumably, it holds a hash reference. So yes, %$arg_ref deferences that hash reference. Another way to write it is %{$arg_ref}. This makes the intent of the code a bit more clear, though more verbose.
To quote from perldata(1):
Scalar values are always named with '$', even when referring
to a scalar that is part of an array or a hash. The '$'
symbol works semantically like the English word "the" in
that it indicates a single value is expected.
$days # the simple scalar value "days"
$days[28] # the 29th element of array #days
$days{'Feb'} # the 'Feb' value from hash %days
$#days # the last index of array #days
So your example would be:
%$arg_ref # hash dereferenced from the value "arg_ref"
my($arg_ref) = #_; grabs the first item in the function's argument stack and places it in a local variable called $arg_ref. The caller is responsible for passing a hash reference. A more canonical way to write that is:
my $arg_ref = shift;
To create a hash reference you could start with a hash:
some_sub(\%hash);
Or you can create it with an anonymous hash reference:
some_sub({pi => 3.14, C => 4}); # Pi is a gross approximation.
Instead of dereferencing the entire hash like that, you can grab individual items with
$arg_ref->{key}
A good brief introduction to references (creating them and using them) in Perl is perldoc perfeftut. You can also read it online (or get it as a pdf). (It talks more about references in complex data structures than in terms of passing in and out of subroutines, but the syntax is the same.)
my %hash = ( fred => 'wilma',
barney => 'betty');
my $hashref = \%hash;
my $freds_wife = $hashref->{fred};
my %hash_copy = %$hash # or %{$hash} as noted above.
Soo, what's the point of the syntax flexibility? Let's try this:
my %flintstones = ( fred => { wife => 'wilma',
kids => ['pebbles'],
pets => ['dino'],
}
barney => { # etc ... }
);
Actually for deep data structures like this it's often more convenient to start with a ref:
my $flintstones = { fred => { wife => 'Wilma',
kids => ['Pebbles'],
pets => ['Dino'],
},
};
OK, so fred gets a new pet, 'Velociraptor'
push #{$flintstones->{fred}->{pets}}, 'Velociraptor';
How many pets does Fred have?
scalar # {flintstones->{fred}->{pets} }
Let's feed them ...
for my $pet ( # {flintstones->{fred}->{pets} } ) {
feed($pet)
}
and so on. The curly-bracket soup can look a bit daunting at first, but it becomes quite easy to deal with them in the end, so long as you're consistent in the way that you deal with them.
Since it's somewhat clear this construct is being used to provide a hash reference as a list of named arguments to a sub it should also be noted that this
sub foo {
my ($arg_ref) = #_;
# do something with $arg_ref->{keys}
}
may be overkill as opposed to just unpacking #_
sub bar {
my ($a, $b, $c) = #_;
return $c / ( $a * $b );
}
Depending on how complex the argument list is.