Experimental keys on scalar is now forbidden - perl

I am trying to debug a Perl script that is giving me the error
Experimental keys on scalar is now forbidden
This seems to be an issue my system having a newer version of perl, but was hoping some can sugegst a quick fix.
The line in question is
foreach my $elemName(keys $grammar -> {$groupName})
grammer was defined as
my $grammar = {};
and groupName comes from
foreach my $groupName (keys %$grammar)
I do not know much about perl, so any help would be appreciated
I have a similar issue with this line
push($DbRef->{def_param}{$par_descr}->{dataset}, $blkDs);
with error
Experimental push on scalar is now forbidden ... near "$blkDs)"
I have tried some solutions I have found about dereferencing, but the syntax is very confusing to me.

keys EXPR and push EXPR, LIST were introduced in 5.14 as an experimental feature.
keys EXPR and push EXPR, LIST started warning in 5.20 when the concept of warnings for experimental features was introduced.
keys EXPR and push EXPR, LIST were removed in 5.24 as the experiment was deemed a failure. (The design of keys EXPR is fundamentally flawed.)
To get the keys of hash, you can use keys %NAME. You can also use a hash deference, such as keys %BLOCK or keys EXRP->%*.
To get the indexes of an array, you can use keys #NAME. You can also use an array deference, such as keys #BLOCK or keys EXRP->#*.
To push scalars onto an array, you can use push #NAME, LIST.
You can also use an array deference, such as push %BLOCK, LIST or push EXRP->#*, LIST.
In your case, you want
keys %{ $grammar -> {$groupName} }
or
keys $grammar -> {$groupName} -> %* # 5.24+
and
push(#{ $DbRef->{def_param}{$par_descr}->{dataset} }, $blkDs);
or
push($DbRef->{def_param}{$par_descr}->{dataset}->#*, $blkDs); # 5.24+

Related

Experimental push on scalar is now forbidden at indent2tree

I realize similar questions have been asked. However, in this case I wish to use this open source perl script:
https://github.com/bAndie91/tools/blob/master/usr/bin/indent2tree
This line is producing the error Experimental push on scalar is now forbidden at /usr/local/bin/indent2tree line 43, near "};"
push $ForkPoint->{subtree}, {data=>$data, parent=>$ForkPoint, subtree=>[]};
I am not very familiar with Perl. I did check several questions on this topic and I tried to fix the issue a few different ways without success.
For example:
push #ForkPoint->{subtree}, {data=>$data, parent=>$ForkPoint, subtree=>[]};
That still produces the error.
Since my goal here is to just use the tool, maybe someone who is familiar with Perl can share the solution. I opened a bug at the project issue page.
You need
push #{ $ForkPoint->{subtree} }, ...
Whenever you can use the name of a variable, you can use a block that evaluates to a reference instead. That means the following are valid syntax for specifying an array:
#NAME # If you have the name
#BLOCK # If you have a reference
That means that the following two snippets are equivalent:
push #arary, ...
my $ref = \#array;
push #{ $ref }, ...
While not relevant in this case, you can omit the curlies when the only thing in the block is a simple scalar ($NAME or $BLOCK).
push #$ref, ...
See Perl Dereferencing Syntax.

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 "keys on reference is experimental" warning

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.

Change ref of hash in Perl

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?
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").
It 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; simply use the hash reference.
This question is really old, but Perl now allows this sort of thing as an experimental feature:
use v5.22;
use experimental qw(refaliasing);
my $first = {
foo => 'bar',
baz => 'quux',
};
\my %hash = $first;
Create named variable aliases with ref aliasing
Mix assignment and reference aliasing with declared_refs
Yes, but…
References in Perl are scalars. You are trying to alias 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 should be avoided”) and won't 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, 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.

Is there any advantage to using keys #array instead of 0 .. $#array?

I was quite surprised to find that the keys function happily works with arrays:
keys HASH
keys ARRAY
keys EXPR
Returns a list consisting of all the keys of the named hash, or the
indices of an array. (In scalar context, returns the number of keys or
indices.)
Is there any benefit in using keys #array instead of 0 .. $#array with respect to memory usage, speed, etc., or are the reasons for this functionality more of a historic origin?
Seeing that keys #array holds up to $[ modification, I'm guessing it's historic :
$ perl -Mstrict -wE 'local $[=4; my #array="a".."z"; say join ",", keys #array;'
Use of assignment to $[ is deprecated at -e line 1.
4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29
Mark has it partly right, I think. What he's missing is that each now works on an array, and, like the each with hashes, each with arrays returns two items on each call. Where each %hash returns key and value, each #array also returns key (index) and value.
while (my ($idx, $val) = each #array)
{
if ($idx > 0 && $array[$idx-1] eq $val)
{
print "Duplicate indexes: ", $idx-1, "/", $idx, "\n";
}
}
Thanks to Zaid for asking, and jmcnamara for bringing it up on perlmonks' CB. I didn't see this before - I've often looped through an array and wanted to know what index I'm at. This is waaaay better than manually manipulating some $i variable created outside of a loop and incremented inside, as I expect that continue, redo, etc., will survive this better.
So, because we can now use each on arrays, we need to be able to reset that iterator, and thus we have keys.
The link you provided actually has one important reason you might use/not use keys:
As a side effect, calling keys() resets the internal interator of the HASH or ARRAY (see each). In particular, calling keys() in void context resets the iterator with no other overhead.
That would cause each to reset to the beginning of the array. Using keys and each with arrays might be important if they ever natively support sparse arrays as a real data-type.
All that said, with so many array-aware language constructs like foreach and join in perl, I can't remember the last time I used 0..$#array.
I actually think you've answered your own question: it returns the valid indices of the array, no matter what value you've set for $[. So from a generality point of view (especially for library usage), it's more preferred.
The version of Perl I have (5.10.1) doesn't support using keys with arrays, so it can't be for historic reasons.
Well in your example, you are putting them in a list; So, in a list context
keys #array will be replaced with all elements of array
whereas 0 .. $#array will do the same but as array slicing; So, instead $array[0 .. $#array] you can also mention $array[0 .. (some specific index)]