create my own max and explicit package name requires in perl - perl

I'm not use to coding in perl (it's the first time) but I have to work on a small part of code who someone send me, but I have this error which seem to be a common error for newbie :
Global symbol "$x" requires explicit package name at globalAlgt.pl line 30.
Global symbol "$y" requires explicit package name at globalAlgt.pl line 30.
On the line 30 I have this :
my $max = ($x, $y)[$x < $y];
which seem to be a function max created. I think it's because there is use strict; in the code.
Can you explain me this error ?
Thank you !

That can be re-written as a function
my $max = sub { $_[ $_[0] < $_[1] ] };
which can then be invoked as
print $max->(3,5), "\n";
print $max->(5,3), "\n";
At least, that's what I understand by "define my own max" and "function max".
Or, you can just use List::Util::max.

Related

Perl eval scope

According to perldoc, String Eval should be performed in the current scope. But the following simple test seems to contradict this.
We need the following two simple files to set up the test. Please put them under the same folder.
test_eval_scope.pm
package test_eval_scope;
use strict;
use warnings;
my %h = (a=>'b');
sub f1 {
eval 'print %h, "\n"';
# print %h, "\n"; # this would work
# my $dummy = \%h; # adding this would also work
}
1
test_eval_scope.pl
#!/usr/bin/perl
use File::Basename;
use lib dirname (__FILE__);
use test_eval_scope;
test_eval_scope::f1();
When I run the program, I got the following error
$ test_eval_scope.pl
Variable "%h" is not available at (eval 1) line 1.
My question is why the variable %h is out of scope.
I have done some modification, and found the following:
If I run without eval(), as in the above comment, it will work.
meaning that %h should be in the scope.
If I just add a seemingly useless mentioning in the code, as in the above
comment, eval() will work too.
If I combine pl and pm file into one file, eval() will work too.
If I declare %h with 'our' instead of 'my', eval() will work too.
I encountered this question when I was writing a big program which parsed user-provided code during run time. I don't need solutions as I have plenty workarounds above. But I cannot explain why the above code doesn't work. This affects my perl pride.
My perl version is v5.26.1 on linux.
Thank you for your help!
Subs only capture variables they use. Since f1 doesn't use %h, it doesn't capture it, and %h becomes inaccessible to f1 after it goes out of scope when the module finishes executing.
Any reference to the var, including one that's optimized away, causes the sub to capture the variable. As such, the following does work:
sub f1 {
%h if 0;
eval 'print %h, "\n"';
}
Demo:
$ perl -M5.010 -we'
{
my $x = "x";
sub f { eval q{$x} }
sub g { $x if 0; eval q{$x} }
}
say "f: ", f();
say "g: ", g();
'
Variable "$x" is not available at (eval 1) line 1.
Use of uninitialized value in say at -e line 8.
f:
g: x

in Perl, how to assign the print function to a variable?

I need to control the print method using a variable
My code is below
#!/usr/bin/perl
# test_assign_func.pl
use strict;
use warnings;
sub echo {
my ($string) = #_;
print "from echo: $string\n\n";
}
my $myprint = \&echo;
$myprint->("hello");
$myprint = \&print;
$myprint->("world");
when I ran, I got the following error for the assignment of print function
$ test_assign_func.pl
from echo: hello
Undefined subroutine &main::print called at test_assign_func.pl line 17.
Looks like I need to prefix a namespace to print function but I cannot find the name space. Thank you for any advice!
print is an operator, not a sub.
perlfunc:
The functions in this section can serve as terms in an expression. They fall into two major categories: list operators and named unary operators.
Perl provides a sub for named operators that can be duplicated by a sub with a prototype. A reference to these can be obtained using \&CORE::name.
my $f = \&CORE::length;
say $f->("abc"); # 3
But print isn't such an operator (because of the way it accepts a file handle). For these, you'll need to create a sub with a more limited calling convention.
my $f = sub { print #_ };
$f->("abc\n");
Related:
What are Perl built-in operators/functions?
As mentioned in CORE, some functions can't be called as subroutines, only as barewords. print is one of them.

How to print rows information from two dimensional array in Perl?

I have the following two dimensional array (file.txt):
Code Element Repetitions
AL Train 23
BM Car 30
CN Bike 44
From an input (Code) given by the user, I want to extract the
corresponding Element information.
Example input: BM
Example output:Car
I tried with this code but I do not know how to compare the input name with array content. Thank you a lot
#!/usr/bin/perl
use strict;
use warnings;
print("Type code: ");
my $code = <STDIN>;
chomp($code);
my #content;
if(!open(TABLET, "file.txt")){
die "Unable to open the file\n";
}
while(<TABLET>){
chomp;
push #content, [split / /];
}
foreach my $row ($content) {
if ($content{$code}) {
print "$content{$code}\n";
}
}
close(TABLET);
There are a few problems here. And they can mostly be found by adding use strict to your code. The vast majority of experienced Perl programmers will always start their programs with:
use strict;
use warnings;
as these additions will find a huge number of common mistakes that programmers are prone to make.
The first problem can't be found like that. It seems to be a typo. You split your input using split /;+/ but your input file seems to be delimited by whitespace. So change split /;+/ to just split.
Now let's add use strict to your code and see what happens.
$ perl 2d
Global symbol "$content" requires explicit package name (did you forget to declare "my $content"?) at 2d line 20.
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at 2d line 21.
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at 2d line 22.
Execution of 2d aborted due to compilation errors.
Although there are three errors listed here, the second and third ones are both the same. But let's start with the first. Line 20 in my program is:
foreach my $row ($content) {
But what's that $content variable? You don't use that anywhere else. I suspect it's a typo for #content. Let's change that and try again.
$ perl 2d
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at 2d line 21.
Global symbol "%content" requires explicit package name (did you forget to declare "my %content"?) at 2d line 22.
Execution of 2d aborted due to compilation errors.
Ok. That fixed the first problem, but I guess we now have to look at the repeated error. This is generated by lines 21 and 22, which look like this:
if ($content{$code}) {
print "$content{$code}\n";
Obviously, there's no mention of %content on either of those lines - so what's the problem?
Well, the problem is that %content is mentioned on both of those lines, but it's disguised as $content{$code} in both cases. You have an array called #content and you'd look up values in that array using syntax like $content[0]. The face that you're using {...} instead of [...] means that you're looking in %content, not #content (in Perl you're allowed to have an array and a hash - and also a scalar - all with the same name, which is always a terrible idea!)
But we can't just change $content{$code} to $content[$code] because $code is string ("BM") and array indexes are integers. I we need to rethink this from scratch and actually store the data in %content, not #content. And, actually, I think that makes the code simpler.
#!/usr/bin/perl -w
use strict;
use warnings;
print("Type code: ");
my $code = <STDIN>;
chomp($code);
my %content;
if (!open(TABLET, "file.txt")){
die "Unable to open the file\n";
}
while(<TABLET>){
chomp;
my #record = split;
$content{$record[0]} = \#record;
}
if (exists $content{$code}) {
print "$content{$code}[1]\n";
} else {
print "$code is not a valid code\n";
}
close(TABLET);
We can clean that up a bit (for example, by using lexical filehandles and the three-arg version of open()) to get this:
#!/usr/bin/perl
use strict;
use warnings;
print("Type code: ");
chomp( my $code = <STDIN> );
my %content;
open my $tablet_fh, '<', 'file.txt'
or die "Unable to open the file\n";
while(<$tablet_fh>){
chomp;
my #record = split;
$content{$record[0]} = \#record;
}
if (exists $content{$code}) {
print "$content{$code}[1]\n";
} else {
print "$code is not a valid code\n";
}

Escaping semi-colon syntax in Perl

How can I escape a semi-colon (that's syntax ) in perl by only adding characters after it?
Say I have a line of code:
print "foo";
I want to add the following code after it so it can repeat 5 times:
print "foo"; x 5;
Is there anyway I can escape/ignore the semicolon (without altering the original piece of code) so it can be interpreted as:
print "foo" x 5;
Edit: This seems like too much of a hassle, better off to just nest the line in a for loop.
Seems like you just want to print "foo" 5 times ?
If yes, then :
use strict;
use warnings;
print "foo" for (1..5);
Yields :
foofoofoofoofoo
Here is an example using a source filter.
In fact it alters the code before executing it so you don't have to do it yourself.
echo package FiveTimes; use Filter::Simple sub{s/;/x5;/g};1; > FiveTimes.pm
perl -MFiveTimes -e"print qq/foo/;"
foofoofoofoofoo
print "foo"; BEGIN{tie *STDOUT,'FiveTimer';sub FiveTimer::TIEHANDLE{bless{},'FiveTimer'}sub FiveTimer::PRINT{CORE::print STDERR $_[1] x 5}}
or more readably
print "foo"; BEGIN{ tie *STDOUT,'FiveTimer' }
sub FiveTimer::TIEHANDLE{bless{},'FiveTimer'}
sub FiveTimer::PRINT{CORE::print STDERR $_[1] x 5}}

How can I pass parameters to Perl subroutines defined using eval?

I'm using a config file (in YAML) to define types that are used later on to validate other config values required for my app:
---
action: >
use List::MoreUtils;
my $value = $_;
any { $value eq $_ } qw(fatal keep merge non-fatal replace);
dir : return defined $_ ? -d $_ : -1;
file : return defined $_ ? -f $_ : -1;
string: 1;
---
config-element:
value: foo
type : file
etc ...
The idea is to eval each type definition, throw them into a hash and then call to validate configuration data (the following is schematic for easy comprehensibility):
#throw sub refs into hash
my %type_sub;
foreach my $key (keys %$type_def_ref) {
my $sub_str = "sub {$type_def_ref->{$key}}";
$type_sub{$key} = eval $sub_str;
}
#validate (myfile is a real file in the cwd)
print $type_sub{file}->('myfile'),"\n";
print $type_sub{action}->('fatal'), "\n";
The problem is that the subroutines in %type_sub don't seem to accept parameters. In the above case, the first print statement outputs -1 while the second outputs:
Use of uninitialized value $value in string eq at (eval 15) line 1.
Use of uninitialized value $_ in string eq at (eval 15) line 1.
Can't call method "any" without a package or object reference at
(eval 15) line 1.
which is not at all what I expect, yet the subroutines are being called.
What am I doing wrong?
EDIT:
I was being sloppy and everything works fine now. Thanks to Friedo.
Don't write code in configuration. Create a library with the code and simply configure which subroutine name you want to use. That should save you an huge amount of work translating strings to code and managing the process. It also saves you a ton of time tracking down problems when someone adjusts the configuration and introduces a syntax error.
I talk about this extensively in the "Configuration" chapter in Mastering Perl, as well as the chapters on dynamic subroutines.
Code doesn't belong in configuration. Say that until you believe it.
Your subroutine parameters will be in the #_ array, not $_. To get the first parameter, look in $_[0] or do my $foo = shift;. (shift operates on #_ by default.)
As for any, I believe the problem is due to any not being able to load its prototype at runtime (subroutine prototypes can only be called at compile-time.) You may need to use explicit parens and an explicit subroutine reference:
any( sub { $value eq $_ }, qw(fatal keep merge non-fatal replace) );