Perl "keys on reference is experimental" warning - perl

My perl program is throwing some warnings, and I am yet to have any luck searching the Internet for a solution. Is there any way I can rewrite the following code snippet so that no warnings are thrown?
"keys on reference is experimental at...":
foreach my $key ( keys %$api_decoded_result{'query'}->{'pages'} ) {
#words = split / /, $api_decoded_result->{'query'}->{'pages'}{$key}->{'extract'};
}

Yup. This is because of precedence of operator dereferencing. %$api_decoded_result binds tighter than the {'query'}.
keys %{$api_decoded_result{'query'}->{'pages'}}
Will do what you want.

Related

Missing expression in split syntax in perl

I haven't done much (any really) perl programming and today someone sent me some code to review in perl. Mostly I can understand what's happening but there was one line that I simply cannot get my head around, the split function in the code block below
while(<>) {
chomp;
my($v1, $v2, $v3, $v4) = split(/,/);
# ....
I read this blog post and it suggests that the syntax is valid (and I believe that it is). But what I don't understand is what exactly is being split with the Expression missing
The split documentation explains what happens when each argument is omitted.
If only PATTERN is given, EXPR defaults to $_.
This code uses a number of shortcuts. It is equivalent to this:
while(defined($_ = readline(ARGV))) {
chomp($_);
my($v1, $v2, $v3, $v4) = split(/,/, $_, 5);

Use specified version of perl

I am using a script that traverses a HoH:
for my $bp (sort { $a <=> $b } keys $vars{$chr1}){
for my $chr2 (sort keys $vars{$chr1}{$bp}){
[...]
}
}
On my local computer (running perl v5.18.2) this is accepted syntax. However, I am concerned about the portability of this section, as when I run this on my cluster (running perl v5.24.0), the script dies with:
Experimental keys on scalar is now forbidden at script/mergesvs.pl
line 66.
As I understand it, I can fix this by changing the above to:
for my $bp (sort { $a <=> $b } keys %{ $vars{$chr1} }){
for my $chr2 (sort keys %{ $vars{$chr1}{$bp} }){
[...]
}
}
But as I use the former syntax more often, I would prefer to use a version of perl compatible with this (e.g. v5.18.2) in this script.
I've tried adding use 5.18.2 at the top of the script, but it makes no difference. Where should I specify what version of perl to use?
Or should I adopt the newer style in all of my code?
You can test for the version of perl with $] and do something like:
use v5.18.2;
if ($] >= 5.024) {
die("You are using a version of Perl too new for this program");
}
… but this is a terrible idea. You should be future proofing your code, not designing it to blow up if Perl is too new.
As the error message says, it was an experimental feature. It turned out to have lots of different problems.
You should stop using it and go back to using explicit dereferencing (which is not the new syntax, it is the original syntax after the new syntax turned out to be a bad idea).
You can specify a minimum version with use and a maximum version with no.
use v5.14.0; # die if $] < 5.014 # When C<< keys EXPR >> was introduced
no v5.24.0; # die if $] >= 5.024 # When C<< keys EXPR >> was removed
no if $] >= 5.020, warnings => qw( experimental::autoderef );
but this is a terrible idea ... what Quentin said ...

Perl: How to get keys on key "keys on reference is experimental"

I am looking for a solution to Perl's warning
"keys on reference is experimental at"
I get this from code like this:
foreach my $f (keys($normal{$nuc}{$e})) {#x, y, and z
I found something similar on StackOverflow here:
Perl throws "keys on reference is experimental"
but I don't see how I can apply it in my situation.
How can I get the keys to multiple keyed hashes without throwing this error?
keys %{$normal{$nuc}{$e}}
E.g. dereference it first.
If you had a reference to start off with, you don't need {} E.g.:
my $ref = $normal{$nuc}{$e};
print keys %$ref;
The problem is that $normal{$nuc}{$e} is a hash reference, and keys will officially only accept a hash. The solution is simple—you must dereference the reference—and you can get around this by writing
for my $f ( keys %{ $normal{$nuc}{$e} } ) { ... }
but it may be wiser to extract the hash reference into an intermediate variable. This will make your code much clearer, like so
my $hr = $normal{$nuc}{$e};
for my $f ( keys %$hr ) { ... }
I would encourage you to write more meaningful variable names. $f and $e may tell you a lot while you're writing it, but it is sure to cause others problems, and it may even come back to hit you a year or two down the line
Likewise, I am sure that there is a better identifier than $hr, but I don't know the meaning of the various levels of your data structure so I couldn't do any better. Please call it something that's relevant to the data that it points to
keys $hash_ref
is the reason for the warning keys on references is experimental
Solution:
keys %{ $hash_ref }
Reference:
https://www.perl.com/pub/2005/07/14/bestpractices.html/

perl s/this/that/r ==> "Bareword found where operator expected"

Perl docs recommend this:
$foo = $bar =~ s/this/that/r;
However, I get this error:
Bareword found where operator expected near
"s/this/that/r" (#1)
This is specific to the r modifier, without it the code works.
However, I do not want to modify $bar.
I can, of course, replace
my $foo = $bar =~ s/this/that/r;
with
my $foo = $bar;
$foo =~ s/this/that/;
Is there a better solution?
As ruakh wrote, /r is new in perl 5.14. However you can do this in previous versions of perl:
(my $foo = $bar) =~ s/this/that/;
There's no better solution, no (though I usually write it on one line, since the s/// is essentially serving as part of the initialization process:
my $foo = $bar; $foo =~ s/this/that/;
By the way, the reason for your error-message is almost certainly that you're running a version of Perl that doesn't support the /r flag. That flag was added quite recently, in Perl 5.14. You might find it easier to develop using the documentation for your own version; for example, http://perldoc.perl.org/5.12.4/perlop.html if you're on Perl 5.12.4.
For completeness.
If you are stuck with an older version of perl.
And really want to use the s/// command without resorting to using a temporary variable.
Here is one way:
perl -E 'say map { s/_iter\d+\s*$//; $_ } $ENV{PWD}'
Basically use map to transform a copy of the string and return the final output.
Instead of what s/// does - of returning the count of substitutions.

Hash of hashes in perl

I am trying to use a hash of hashes like this -
#!/usr/bin/perl -w
use strict;
my %hash = ();
sub hash_populate
{
my $name = "PQR,ABD,XYZ";
my #parts = split(/,/,$name);
my $i = $parts[0];
my $a= $parts[1];
my $b = $parts[2];
$hash{$i} = {"A" =>$a,"B" => $b};
my $c = $hash{$i}{"A"};
print $c;
}
I get an error of the form
Can't use string ("HASH(0x16c43c)") as a HASH ref while "strict refs" in use
The same code works when use strict is not present. Can someone tell me why?
Well since I tried this in 5.8.7 with strict and it worked, I can't help thinking that the code you're actually running was malformed in some way this is not and "It worked without strict" means that it didn't die. Perl just let you do whatever you wanted, and lets you catch the problems yourself.
So the answer is
1) the code works (for toy code) for 5.8
2) "it worked without strict" is a common statement among Perl newbies, and until I can see some code that tries to stringify a hash reference I can't say anything different.
3) Why it "works without strict" is a combination of how much you fit that pattern, how the actual code is malformed, and the fact that Perl will allow you to shoot yourself in the foot myriad times with strict turned off--and some times you'll think that it worked.
4) Somethings actually do work without strict, and they are meant to. That is turning strictures off (no strict 'refs';) is meant to be the way that you purposely do a chancy operation.