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 ...
Related
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.
While reading a book on advanced Perl programming(1), I came across
this code:
while (defined($s = <>)) {
...
Is there any special reason for using defined here? The documentation for
perlop says:
In these loop constructs, the assigned value (whether assignment is
automatic or explicit) is then tested to see whether it is defined. The
defined test avoids problems where line has a string value that would be
treated as false by Perl, for example a "" or a "0" with no trailing
newline. If you really mean for such values to terminate the loop, they
should be tested for explicitly: [...]
So, would there be a corner case or that's simply because the book is too old
and the automatic defined test was added in a recent Perl version?
(1) Advanced Perl Programming, First Edition, Sriram Srinivasan. O'Reilly
(1997)
Perl has a lot of implicit behaviors, many more than most other languages. Perl's motto is There's More Than One To Do It, and because there is so much implicit behavior, there is often More Than One Way To express the exact same thing.
/foo/ instead of $_ =~ m/foo/
$x = shift instead of $x = shift #_
while (defined($_=<ARGV>)) instead of while(<>)
etc.
Which expressions to use are largely a matter of your local coding standards and personal preference. The more explicit expressions remind the reader what is really going on under the hood. This may or may not improve the readability of the code -- that depends on how knowledgeable the audience is and whether you are using well-known idioms.
In this case, the implicit behavior is a little more complicated than it seems. Sometimes perl will implicitly perform a defined(...) test on the result of the readline operator:
$ perl -MO=Deparse -e 'while($s=<>) { print $s }'
while (defined($s = <ARGV>)) {
print $s;
}
-e syntax OK
but sometimes it won't:
$ perl -MO=Deparse -e 'if($s=<>) { print $s }'
if ($s = <ARGV>) {
print $s;
}
-e syntax OK
$ perl -MO=Deparse -e 'while(some_condition() && ($s=<>)) { print $s }'
while (some_condition() and $s = <ARGV>) {
print $s;
}
-e syntax OK
Suppose that you are concerned about the corner cases that this implicit behavior is supposed to handle. Have you committed perlop to memory so that you understand when Perl uses this implicit behavior and when it doesn't? Do you understand the differences in this behavior between Perl v5.14 and Perl v5.6? Will the people reading your code understand?
Again, there's no right or wrong answer about when to use the more explicit expressions, but the case for using an explicit expression is stronger when the implicit behavior is more esoteric.
Say you have the following file
4<LF>
3<LF>
2<LF>
1<LF>
0
(<LF> represents a line feed. Note the lack of newline on the last line.)
Say you use the code
while ($s = <>) {
chomp;
say $s;
}
If Perl didn't do anything magical, the output would be
4
3
2
1
Note the lack of 0, since the string 0 is false. defined is needed in the unlikely case that
You have a non-standard text file (missing trailing newline).
The last line of the file consists of a single ASCII zero (0x30).
BUT WAIT A MINUTE! If you actually ran the above code with the above data, you would see 0 printed! What many don't know is that Perl automagically translates
while ($s = <>) {
to
while (defined($s = <>)) {
as seen here:
$ perl -MO=Deparse -e'while($s=<DATA>) {}'
while (defined($s = <DATA>)) {
();
}
__DATA__
-e syntax OK
So you technically don't even need to specify defined in this very specific circumstance.
That said, I can't blame someone for being explicit instead of relying on Perl automagically modifying their code. After all, Perl is (necessarily) quite specific as to which code sequences it will change. Note the lack of defined in the following even though it's supposedly equivalent code:
$ perl -MO=Deparse -e'while((), $s=<DATA>) {}'
while ((), $s = <DATA>) {
();
}
__DATA__
-e syntax OK
while($line=<DATA>){
chomp($line);
if(***defined*** $line){
print "SEE:$line\n";
}
}
__DATA__
1
0
3
Try the code with defined removed and you will see the different result.
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.
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.
I like Data::Alias. It seems to be broken in 5.12. Can it be fixed? Will it be fixed soon? Is there any nice alternative?
Any version of Data::Alias built before Version 1.08 (Released October 22nd, 2010 BST) won't work with Perl 5.12 as Data::Alias prior to 1.08 is broken in Perl 5.12. Upgrade to the latest version (1.08 or newer) and it should work!
As an interesting side note, it seems like being able to do aliases may be coming to Perl as a language feature in the future, with the cleanup of := no longer meaning an empty attribute list. Look forward to it! :)
The module hasn't been updated since 2007 but you can always send a message to the author (Matthijs van Duin: xmath#cpan.org) or file a bug report as Robert mentioned in his answer.
Here are some alternatives:
As far as additional CPAN modules for aliasing that work in 5.12+:
Variable::Alias - clean syntax
Tie::Alias - a pure perl solution
Lexical::Alias - clean syntax
Devel::LexAlias - a bit lower level
And searching for 'alias' on CPAN turns up a few more, none seem to provide the "do everything with aliases in this statement" feature of Data::Alias though. So until Data::Alias is fixed, you can use one of the above, or the following pure Perl methods:
Perl has built in support for aliasing any variable to variables that exist in the symbol table. This is done as follows:
my $x = 1;
our $y; # declare $y in the symbol table for the current package
{
local *y = \$x; # make $y an alias of $x in the current scope
$y++;
}
print $x; # prints 2
But as always, be aware of what dynamic scope / local actually does before using it.
A lexical scalar can be used as an alias within the scope of a for loop:
my $x = 1;
for my $y ($x) {
$y++;
}
print $x; # prints 2
this type of lexical alias can even be passed out of the loop in a closure if needed
You can create array aliases using Perl's aliasing magic for subroutine argument lists:
my $x = 1;
my $alias = sub{\#_}->($x); # return a reference to its argument list,
# which maintains its aliases
$$alias[0]++;
print $x; # prints 2
but that doesn't really give you any more functionality than references, just with a different syntax.
And an example using Perl's references:
my $x = 1;
my $y = \$x; # take a reference to $x
$$y++; # dereference $y
print $x; # prints 2
I just found another potential option: Scalar::Alias, which seems to work in Perl 5.12. Obviously, it only aliases scalars, but it doesn't require a fat comma in place of an equals sign.