Why does $a become an arrayref? I'm not pushing anything to it.
perl -MData::Dumper -e 'use strict; 1 for #$a; print Dumper $a'
$VAR1 = [];
It is because the for loop treats contents of #$a as lvalues--something that you can assign to. Remember that for aliases the contents of the array to $_. It appears that the act of looking for aliasable contents in #$a, is sufficient to cause autovivification, even when there are no contents to alias.
This effect of aliasing is consistent, too. The following also lead to autovivification:
map {stuff} #$a;
grep {stuff} #$a;
a_subroutine( #$a);
If you want to manage autovivification, you can use the eponymous pragma to effect lexical controls.
When you treat a scalar variable whose value is undef as any sort of reference, Perl makes the value the reference type you tried to use. In this case, $a has the value undef, and when you use #$a, it has to autovivify an array reference in $a so you can dereference it as an array reference.
$a becomes an ARRAY reference due to Perl's autovivification feature.
$a and $b are special variables in Perl (used in sort) and have a special scope of their own.
perl -MData::Dumper -e 'use strict; 1 for #$c; print Dumper $c'
produces
Global symbol "$c" requires explicit package name at -e line 1.
Global symbol "$c" requires explicit package name at -e line 1.
Execution of -e aborted due to compilation errors.
Related
Following code crashes as expected, because $x does not hold Array reference:
$ perl -E 'use strict; use warnings; my $x; say #{$x}; say "OK";'
Can't use an undefined value as an ARRAY reference at -e line 1.
However this code works without any warning:
perl -E 'use strict; use warnings; my $x; say for #{$x}; say "OK";'
OK
Why? I have not found any documentation of this behavior in perldoc.
Looks like for context is implying some DWIM-ness logic here (with dangerous consequences).
This is hopefully somewhat enlightening:
$ perl -E 'use strict; use warnings; my $x; say for #{$x}; say $x;'
ARRAY(0x561b92479420)
The $x variable is being autovivified by for.
Compare with:
$ perl -E 'use strict; use warnings; my $x; $x->[0]; say $x;'
Useless use of array element in void context at -e line 1.
ARRAY(0x561b92479420)
Certain reference operations on undefined scalars will cause autovivification to happen. for #{$var} is apparently one of them. There is a autovivication module on CPAN that allows you more control over when autovivification happens.
You have this statement:
say for #{$x}
The value of $x is undef, so when you dereference it, Perl notices that you want to use $x as an array ref. It then creates the array for you ("autovivification"). That array is empty, so there's nothing that for can iterate over.
Autovivification is the feature that enables us to do tasks like counting in this loop. When a key for $first doesn't exist, Perl adds that key to the hash. But, there needs to be a second level hash where $second can be a key. Since the value of the $first key is undef, Perl autovivifies that second level hash so you can operate on it:
for ( ... ) {
# source.example.com dest.example.org 1234
my( $first, $second, $count ) = split;
$hash->{$first}{$second} += $count;
}
Image how hard this task would be if you had to know all the keys, hash sizes, and so on in advance, or had to extend or modify the data structure sizes yourself.
We have an extended example of this in Intermediate Perl, the book in the tutorial series where we talk about references. I also write about it in Understand autovivification.
#tobyink mentions the autovificiation pragma. It recognizes certain constructs and can warn or stop your program:
use v5.10;
use warnings;
no autovivification qw(store);
my $x;
for ( #$x ) {
say "Got value: $_";
}
say "End. x = $x";
Now the program stops:
Can't vivify reference at ...
But, all of this makes your program a bit slower as Perl has to do extra work to check all that.
There's also the StackOverflow answer How do I disable autovivification in Perl?, but I think the answers aren't that useful.
You should get used to autovivification and programming in an environment where it exists (along with all the other things that you can accidentally do to create bugs).
Code
$ cat test1
hello
i am
lazer
nananana
$ cat 1.pl
use strict;
use warnings;
my #fh;
open $fh[0], '<', 'test1', or die $!;
my #res1 = <$fh[0]>; # Way1: why does this not work as expected?
print #res1."\n";
my $fh2 = $fh[0];
my #res2 = <$fh2>; # Way2: this works!
print #res2."\n";
Run
$ perl 1.pl
1
5
$
I am not sure why Way1 does not work as expected while Way2 does. Aren't those two methods the same? What is happening here?
Because of the dual nature of the <> operator (i.e. is it glob or readline?), the rules are that to behave as readline, you can only have a bareword or a simple scalar inside the brackets. So you'll have to either assign the array element to a simple scalar (as in your example), or use the readline function directly.
Because from perlop:
If what's within the angle brackets is neither a filehandle nor a simple scalar variable containing a filehandle name, typeglob, or typeglob reference, it is interpreted as a filename pattern to be globbed, and either a list of filenames or the next filename in the list is returned, depending on context. This distinction is determined on syntactic grounds alone. That means <$x> is always a readline() from an indirect handle, but <$hash{key}> is always a glob().
You can spell the <> operator as readline instead to avoid problems with this magic.
Anything more complex than a bareword (interpreted as a file handle) or a simple scalar $var is interpreted as an argument to the glob() function. Only barewords and simple scalars are treated as file handles to be iterated by the <...> operator.
Basically the rules are:
<bareword> ~~ readline bareword
<$scalar> ~~ readline $scalar
<$array[0]> ~~ glob "$array[0]"
<anything else> ~~ glob ...
It's because <$fh[0]> is parsed as glob($fh[0]).
Use readline instead:
my #res1 = readline($fh[0]);
Usually bareword as the filehanle or a variable holds filehandle could be places inside <> operator to reference the file, but NOT the filehandle extracted from typeglob as the last line below shows. Why it doesn't work because the last case also references a filehandle?
open FILE, 'file.txt';
my $myfile = *FILE{IO};
print <$myfile>;
print <*FILE{IO}>; # this line doesn't work.
<> is among other things shortcut for readline(), and it accepts simple scalars or bare word, ie. <FILE>. For more complex expressions you have to be more explicit,
print readline *FILE{IO};
otherwise it will be interpreted as glob()
perl -MO=Deparse -e 'print <*FILE{IO}>;'
use File::Glob ();
print glob('*FILE{IO}');
In perlop, it says:
If what's within the angle brackets is neither a filehandle nor a
simple scalar variable containing a filehandle name, typeglob, or
typeglob reference, it is interpreted as a filename pattern to be
globbed ...
Since we want to be able to say things like:
foreach (<*.c>) {
# Do something for each file that matches *.c
}
it is not possible for perl to interpret the '*' as meaning a typeglob.
As noted in the other answer, you can work around this using readline, or you can assign the typeglob to a scalar first (as your example shows).
Why is it that my code is not working after I added use strict; use warnings;? Is there a way to make it work?
Previously, the working code is:
#!/usr/bin/perl -s
print "x: $x\n";
print "y: $y\n";
The command that I ran is perl -s test.pl -x="hello" -y="world". The output is:
x: hello
y: world
However, after I added use strict; use warnings;, I got the following errors:
Variable "$x" is not imported at test.pl line 4.
Variable "$y" is not imported at test.pl line 5.
Global symbol "$x" requires explicit package name at test.pl line 4.
Global symbol "$y" requires explicit package name at test.pl line 5.
Execution of test.pl aborted due to compilation errors.
I know I need to declare my $x and my $y to fix the 3rd and 4th error. But what does the first 2 errors mean and how do I overcome it?
Actually, declaring these variables as lexical (my) variables will not help, because it's too "late": the -s switch will already have set them. It sets the global (package) variables (in your case, $main::x and $main::y, or — as a special shorthand — $::x and $::y). If you don't want to have to refer to them using their package-qualified names, then you can use an our declaration to indicate that the bare names $x and $y refer to the $x and $y in the current package:
our ($x, $y);
print "x: $x\n";
print "y: $y\n";
(Hat-tip to derobert for pointing out that you can use our for this.)
Alternatively, you can copy the global variables into identically-named lexical variables:
my ($x, $y) = ($::x, $::y);
print "x: $x\n";
print "y: $y\n";
This will take care of both sets of diagnostics.
You are using a a rudimentary switch parser perl -s, which uses global variables. To make it work with use strict you need to refer to the globals: $main::x as ruakh pointed out.
But even so, lexical variables (declared with my) are preferable in just about all cases. Just do:
use strict;
use warnings;
my ($x, $y) = #ARGV;
print "x: $x\n";
print "y: $y\n";
And use with:
perl test.pl hello world
For a more detailed and switch like handling, check out the Getopt::Long module.
To understand what ANY Perl error/warning means, you can refer to perldiag.
Specifically, for "is not imported", it says:
Variable "%s" is not imported%s
(W misc) With "use strict" in effect, you referred to a global variable that you apparently thought was imported from another module, because something else of the same name (usually a subroutine) is exported by that module. It usually means you put the wrong funny character on the front of your variable.
Basically, Perl made 2 distinct guesses about your un-declared identifyer $x - it was either
a package-scoped global that is prohibited from being used under strict ("Global symbol "$x" requires explicit package")
or it was an attempt to use another package's variable that ought to have been imported but wasn't ("Variable "$x" is not imported").
Perl can not tell WHICH of the two theories are correct, so spat out both possibilities. The latter error (Global symbol "$x" requires explicit package name) was the correct one in this case - it WAS a global variable in your original pre-strict code.
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.