Initializing Perl variables using eval - perl

I'm guessing this should be something obvious to those knowing Perl, but I simply don't get it... I also guess it has to do with problems described in Perl scoping « darkness - but I cannot apply any of that in my case.
Anyway, here's the code:
#!/usr/bin/env perl
# call with:
# ./test.pl
use strict;
my $tvars = "my \$varA = 1;
my \$varB = 2;
my \$varC = 3;
";
my #lines = split /\n/, $tvars;
foreach my $line (#lines) {
print "$line\n";
eval $line; warn $# if $#;
}
#~ print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 18.
#~ print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 19.
#~ print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 20.
$tvars = "our \$varA = 1;
our \$varB = 2;
our \$varC = 3;
";
#lines = split /\n/, $tvars;
foreach my $line (#lines) {
print "$line\n";
eval $line; warn $# if $#;
}
print "$varA\n"; # Global symbol "$varA" requires explicit package name at ./test.pl line 33.
print "$varB\n"; # Global symbol "$varB" requires explicit package name at ./test.pl line 34.
print "$varC\n"; # Global symbol "$varC" requires explicit package name at ./test.pl line 35.
Simply speaking, I'd like to have something like "$varA = 1;" written as a string (text file); and I'd like perl to eval it, so that afterwards I have access to variable "$varA" in the same script - the errors I get when I try to access those after an eval are in the comments of the code above (however, no warnings are reported during the eval). (I'm guessing, what I'd need is something like "global" variables, if the eval runs in a different context than the main script?)
How would I go about doing that? Do I have to go through all of that package definition business, even for a simple script like the above?

It has everything to do with scoping. The variables are declared with my inside the eval expression. This makes them local to the eval statement and not accessible once the eval statement exits. You can declare them first, though:
my ($varA, $varB, $varC); # declare outside the eval statement
my $tvars = "\$varA = 1;
\$varB = 2;
\$varC = 3;
";
eval $tvars;
# local $varA, $varB, $varC variables are now initialized
or as you suggest, you can use global variables. The easiest (though not necessarily the "best" way) is to prepend :: to all variable names and get them in the main package.
my $tvars = "\$::varA = 1;
\$::varB = 2;
\$::varC = 3;
";
eval $tvars;
print "A=$::varA, B=$::varB, C=$::varC\n";
Now when you tried our variables in your example, you actually were initializing package (global) variables. But outside the eval statement, you still need to qualify (i.e., specify the package name) them in order to access them:
$tvar = "our \$foo = 5";
eval $tvar;
print $main::foo; # ==> 5

The problem is that when you do eval $string, $string is evaluated as its own subroutine which has its own lexical scope. From perldoc -f eval:
In the first form [in which the argument is a string], the return value of EXPR is parsed and
executed as if it were a little Perl program. The value of the expression (which is itself
determined within scalar context) is first parsed, and if there were no errors, executed in the
lexical context of the current Perl program, so that any variable settings or subroutine and format
definitions remain afterwards.
So, in other words, if you have:
use strict;
use warnings;
eval "my $foo=5;";
print "$foo\n";
you'll get an error:
Global symbol "$foo" requires explicit package name at -e line 3.
Global symbol "$foo" requires explicit package name at -e line 4.
However, if you initialize your variables first, you're fine.
use strict;
use warnings;
my $foo;
eval "\$foo=5;";
print "$foo\n"; #prints out 5, as expected.

Related

Given a number of variables to test for definedness, how to (easily) find out the one which was left undefined?

Today I saw this piece of code:
if ( not defined($reply_address)
or not defined($from_name)
or not defined($subject)
or not defined($date) )
{
die "couldn’t glean the required information!";
}
(Jeffrey Friedl, "Mastering Regular Expressions", p. 59, 3rd ed.)
and I thought "How can I know which variable misfired?"
Of course, if there are only 4 variables to test, as in the example above, one could come up with:
if ( not defined $reply_address )
{
die "\$reply_address is not defined"
}
elsif ( not defined $from_name )
{
die "\$from_name is not defined"
}
elsif ...
But what if there are 14 variables? Or 40...?
One still needs to go through all of them, manually testing each and every one?
Isn't there a shorter, more "magical" way of telling which variable was left undefined?
You could create a table to simplify a little bit:
use strict;
use warnings;
my $reply_address = "xyz";
my $from_name;
my $subject = "test";
my $date;
my #checks = (
[\$reply_address, '$reply_adress'],
[\$from_name, '$from_name'],
[\$subject, '$subject'],
[\$date, '$date'],
);
for my $check (#checks) {
if (not defined ${$check->[0]}) {
die $check->[1] . " is not defined";
}
}
You can do what you want with symbolic references, though using them is generally not a great idea, and it can only be done with package variables, not lexically scoped variables (and lexically scoped variables are preferred to package variables -- see this answer for a brief comparison of the two).
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
our($foo1) = 1;
our($bar1) = undef;
our($baz1) = 3;
foreach my $name (qw(foo1 bar1 baz1)) {
{
no strict 'refs';
my($value) = $$name;
warn "$name: is not defined" unless defined $value;
say "$name: <$value>";
}
}
Using warn instead of die for illustrative purposes.
</tmp> $ ./test.pl
foo1: <1>
bar1: is not defined at ./test.pl line 16.
Use of uninitialized value $value in concatenation (.) or string at ./test.pl line 17.
bar1: <>
baz1: <3>
You can also just loop through all of the variables using common code to check them:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
my($foo2) = 1;
my($bar2) = undef;
my($baz2) = 3;
foreach my $vardef (["foo2", $foo2], ["bar2", $bar2], ["baz2", $baz2]) {
my($name) = $vardef->[0];
my($value) = $vardef->[1];
warn "$name: is not defined" unless defined $value;
say "$name: <$value>";
}
which gives similar output:
foo2: <1>
bar2: is not defined at ./test.pl line 29.
Use of uninitialized value $value in concatenation (.) or string at ./test.pl line 30.
bar2: <>
baz2: <3>
Finally, if you can manage to get the variables into a hash, you can loop through the keys of the hash and test them that way:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
my($vars) = {
foo3 => 1,
bar3 => undef,
baz3 => 3,
};
foreach my $name (sort keys %$vars) {
my($value) = $vars->{$name};
warn "$name: is not defined" unless defined $value;
say "$name: <$value>";
}
I threw the sort in there because I like deterministic behavior...
bar3: is not defined at ./test.pl line 42.
Use of uninitialized value $value in concatenation (.) or string at ./test.pl line 43.
bar3: <>
baz3: <3>
foo3: <1>
If the test really was as simple as die if ! defined then I would probably just list them out:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
my($foo4) = 1;
my($bar4) = undef;
my($baz4) = 3;
die qq([ERROR] \$foo4 not defined\n) unless defined $foo4;
die qq([ERROR] \$bar4 not defined\n) unless defined $bar4;
die qq([ERROR] \$baz4 not defined\n) unless defined $baz4;
which just gives us:
[ERROR] $bar4 not defined
The last approach is just very straightforward and unambiguous. If the test is not as dead simple as this, then I'd go with the second approach. If you're worried about a list of 40 (or even 14) checks of this nature, then I'd look at the design.
See also this PadWalker code example for a very complicated version of the first option, but allowing lexically scoped variables.
Could be done with a string-eval:
use strict;
use warnings;
my ($reply_address, $from_name, $subject, $date) = ('', '', undef, '');
for my $var (qw(reply_address from_name subject date)) {
my $defined;
eval "\$defined = defined \$$var";
die "eval failed: $#" if $#;
die "\$$var is not defined" unless $defined;
}

Perl compile time errors depending on the procedural order of subroutines

So I have this file:
casper_mint#casper-mint-dell ~/learn_perl_hard_way $ cat bettypage
foo foo foo foo foo foo foo
boo boo boo
And wanted to read it it and print it between 2 sub routines.
This kept throwing errors:
#!/usr/bin/perl
use strict;
use warnings ;
sub read_file {
my $file = shift ;
open (FILE, $file) || die " Couldn't open $file";
while (my $line = <FILE>) {
read_line $line ;
}
}
sub read_line {
my #list = split " ", shift ;
foreach my $word (#list) {
print "$word\n";
}
}
read_file(#ARGV) ;
casper_mint#casper-mint-dell ~/learn_perl_hard_way $ ./test_hash.pl bettypage
Can't locate object method "read_line" via package "foo foo foo foo foo foo foo" (perhaps you forgot to load "foo foo foo foo foo foo foo"?) at ./test_hash.pl line 13, <FILE> line 1.
casper_mint#casper-mint-dell ~/learn_perl_hard_way $
So I put the "read_line subroutine" before the "read_file subroutine" - since it depends on it, from a procedural point of view and it works just fine.
#!/usr/bin/perl
use strict;
use warnings ;
sub read_line {
my #list = split " ", shift ;
foreach my $word (#list) {
print "$word\n";
}
}
sub read_file {
my $file = shift ;
open (FILE, $file) || die " Couldn't open $file";
while (my $line = <FILE>) {
read_line $line ;
}
}
read_file(#ARGV) ;
I know from working with bash that the subroutines usually has to come first in the code for it to work.
However, I thought that perl compiles the script and then executes it. And by compiling, I did not think that it would matter where the subroutine was located.
That by compiling everything before executing the subroutine would at least be available to be read it by the whole program. If perl compiles the whole script before executing it, why should the order of the subroutine matter - shouldn't the "read_line" subroutine be available to the "read_file" subroutine - regardless of where it is placed in the script?
Unless predeclared, you need to call your subs with parenthesis, ie. read_line($line)
From perlsub
To call subroutines:
1. NAME(LIST); # & is optional with parentheses.
2. NAME LIST; # Parentheses optional if predeclared/imported.
3. &NAME(LIST); # Circumvent prototypes.
4. &NAME; # Makes current #_ visible to called subroutine.
But really, just get into the habit of always using parenthesis (option 1). Your code will thank you later with better readability and less surprises.

Perl, how to read from stdin outside a loop?

There is something I don't understand :
say in is a file containing :
1
2
3
and foo.pl :
use strict;
<>;
print;
<>;
print;
<>;
print;
and then run :
perl foo.pl < in
Why this programm doesn't output anything ?
...While this one :
use strinct;
while(<>) {
print;
}
output the whole file
Because
while(<>)
Is shorthand for
while($_ = <>)
Which means the line is assigned to the default variable $_. Which is also used by print.
What you wrote:
<>;
Does not assign anything to $_. It is just a readline in void context, which means the value is discarded and not stored anywhere. Hence $_ is empty. If you use warnings, Perl will tell you what is going on.
Use of uninitialized value $_ in print
If you do the assignment manually it will work:
$_ = <>;
Note also that you do not have to redirect the file content, you can just supply the file name as an argument:
perl foo.pl in

Perl explicit package name errors in while loop and require troubles

Running this code
parsesendnotes.pl
#!/usr/bin/perl
use strict;
use warnings;
use Device::SerialPort;
use Time::HiRes qw(usleep); # For sleep in ms
if ($#ARGV + 1 != 2) {
print "Usage: $0 port filename\n";
print "Example: $0 /dev/ttyASM0 money.txt\n";
exit 1;
}
my $file = $ARGV[0];
my $dev = $ARGV[1];
if (!-e $file || !-e $dev) {
print "File or brain not found.\n";
exit 1;
}
my $arduino = DeviceSerialPort->new($dev);
$arduino->baudrate(9600);
$arduino->databits(8);
$arduino->parity("none");
$arduino->stopbits(1);
require "frequencies.pl";
open NOTES, "$file";
print $frequencies{"LA3"};
while (<NOTES>) {
chomp; # No newline
s/#.*//; # No comments
s/^\s+//; # No leading white
s/\s+$//; # No trailing white
next unless length;
if ($_ =~ m/^TEMPO/) {
my $tempo = split(/\s+/, $_, -1);
print "Tempo is $tempo.";
} else {
my #tone = split(/\s+/, $_);
}
my $note = $frequencies{$tone[0]};
my $duration = $tone[1]*$tempo;
print "Playing $tone[0] (\#$note Hz) for $tone[1] units ($duration ms).";
while ($note > 255) {
$arduino->write(chr(255));
$note -= 255;
}
$arduino->write(chr($note));
$arduino->write(";");
usleep($duration);
}
frequencies.pl
my %frequencies = (
"PAUSE" => 0,
"B0" => 31,
"DO1" => 33,
"DOD1" => 35,
...
);
I obtain these errors
Global symbol "%frequencies" requires explicit package name at ./parsensendnotes2.pl line 30.
Global symbol "%frequencies" requires explicit package name at ./parsensendnotes2.pl line 44.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 44.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 45.
Global symbol "$tempo" requires explicit package name at ./parsensendnotes2.pl line 45.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 46.
Global symbol "#tone" requires explicit package name at ./parsensendnotes2.pl line 46.
Execution of ./parsensendnotes2.pl aborted due to compilation errors.
What am I doing wrong?
name %frequencies is localized in file frequencies.pl: my lasts until end of block, or end of file.
A better way would be to remove my and do like this:
my %frequencies;
eval { %frequencies = do "frequencies.pl"; }
# must check $! and $# here -- see perldoc -f do`
However, an even better way is to use YAML instead:
freq.yml
---
"PAUSE": 0
"B0": 31
"DO1": 33
"DOD1": 35
And then
use YAML qw(LoadFile);
# ...
my $data = LoadFile("freq.yml");
%frequencies = %$data;
As for #tone, $tempo & co, again, my variable scope is limited to {} block. You should do something like
my $x;
if (...) { $x = ... };
to make $x accessible outside if.
my %frequencies from frequencies.pl is not declared inside parsesendnotes.pl.
You need our %frequencies in your main script. And of course the same for the other variables.
Some documentation:
perldoc -f my
perldoc -f our

Perl assignment with a dummy placeholder

In other languages I've used like Erlang and Python, if I am splitting a string and don't care about one of the fields, I can use an underscore placeholder. I tried this in Perl:
(_,$id) = split('=',$fields[1]);
But I get the following error:
Can't modify constant item in list assignment at ./generate_datasets.pl line 17, near ");"
Execution of ./generate_datasets.pl aborted due to compilation errors.
Does Perl have a similar such pattern that I could use instead of creating a useless temporary variables?
undef serves the same purpose in Perl.
(undef, $something, $otherthing) = split(' ', $str);
You don't even need placeholders if you use Slices:
use warnings;
use strict;
my ($id) = (split /=/, 'foo=id123')[1];
print "$id\n";
__END__
id123
You can assign to (undef).
(undef, my $id) = split(/=/, $fields[1]);
You can even use my (undef).
my (undef, $id) = split(/=/, $fields[1]);
You could also use a list slice.
my $id = ( split(/=/, $fields[1]) )[1];
And just to explain why you get the particular error that you see...
_ is a internal Perl variable that can be used in the stat command to indicate "the same file as we used in the previous stat call". That way Perl uses a cached stat data structure and doesn't make another stat call.
if (-x $file and -r _) { ... }
This filehandle is a constant value and can't be written to. The variable is stored in the same typeglob as $_ and #_.
See perldoc stat.