Lexically importing useful functions in a big script - perl

Sometimes I need a useful utility function, like List::Util::max in the middle of a large program that does lots of stuff. So if I do
use List::Util 'max';
At the top of my program, I'm stuck with that symbol, polluting my whole namespace, even though I only need it in one subroutine.
So I've been thinking of trying a different pattern, instead:
use List::Util ();
# a whole bunch of stuff later...
sub blah {
List::Util->import( 'max' );
$blah = max #foobar;
...
}
There are two problems with this, though. For one, it doesn't automatically unimport at the end of the block (drat.) I would have to undo everything with an unimport.
The other problem is that apparently prototypes don't get applied correctly, so I have to say max( #foobar ) instead of the prettier parenthesisless version.
Is there an easy way to temporarily import symbols for a block, which would automagically make them go away at the end of the block, and which would also handle prototypes correctly?

Just do this, it's much better and cleaner:
package Foo;
use strict; use warnings;
use List::Util 'max';
use namespace::autoclean;
# your method definitions here...
namespace::autoclean will "unimport" the symbol after the package's compilation cycle is done. The call to it in your method will still work, but you have no namespace pollution (the *Foo::max symbol is removed) and calling $obj->max() will fail.
Alternatively, you might want to take a look at Lexical::Import (I know nothing about it; an irc birdie mentioned it).

If you only use max in one subroutine, I wouldn't import it into the namespace at all. My solution is to
use List::Util;
sub blah {
print List::Util::max(#list);
}

You can localize a symbol table entry:
use List::Util ();
#y = qw(1 3 5 -9 4);
sub max { # return maximum *absolute value* of list
my $max = abs(shift);
$max<abs($_) && ($max=$abs($_)) for #_;
return $max;
}
sub max2 {
local *max = *List::Util::max;
return max(#_);
}
print "My max: ", max(#y), "\n"; # ==> 9
print "List::Util::max ", max2(#y), "\n"; # ==> 5

perlfunc implies that no MODULE should do what you want:
sub blah {
use List::Util qw(max);
say max #foobar;
no List::Util;
}
but that doesn't work -- at least not for List::Util. I believe that it would need to define an unimport method. Even then, I'm not sure if you could have a bare max in your module call different definitions.

Related

Can I make a variable optional in a perl sub prototype?

I'd like to understand if it's possible to have a sub prototype and optional parameters in it. With prototypes I can do this:
sub some_sub (\#\#\#) {
...
}
my #foo = qw/a b c/;
my #bar = qw/1 2 3/;
my #baz = qw/X Y Z/;
some_sub(#foo, #bar, #baz);
which is nice and readable, but the minute I try to do
some_sub(#foo, #bar);
or even
some_sub(#foo, #bar, ());
I get errors:
Not enough arguments for main::some_sub at tablify.pl line 72, near "#bar)"
or
Type of arg 3 to main::some_sub must be array (not stub) at tablify.pl line 72, near "))"
Is it possible to have a prototype and a variable number of arguments? or is something similar achievable via signatures?
I know it could be done by always passing arrayrefs I was wondering if there was another way. After all, TMTOWTDI.
All arguments after a semi-colon are optional:
sub some_sub(\#\#;\#) {
}
Most people are going to expect your argument list to flatten, and you are reaching for an outdated tool to do what people don't expect.
Instead, pass data structures by reference:
some_sub( \#array1, \#array2 );
sub some_sub {
my #args = #_;
say "Array 1 has " . $args[0]->#* . " elements";
}
If you want to use those as named arrays within the sub, you can use ref aliasing
use v5.22;
use experimental qw(ref_aliasing);
sub some_sub {
\my( #array1 ) = $_[0];
...
}
With v5.26, you can move the reference operator inside the parens:
use v5.26;
use experimental qw(declared_refs);
sub some_sub {
my( \#array1 ) = $_[0];
...
}
And, remember that v5.20 introduced the :prototype attribute so you can distinguish between prototypes and signatures:
use v5.20;
sub some_sub :prototype(##;#) { ... }
I write about these things at The Effective Perler (which you already read, I see), in Perl New Features, a little bit in Preparing for Perl 7 (which is mostly about what you need to stop doing in Perl 5 to be future proof).

How can I use "member" variables of a module inside a function?

I have this code:
#!/usr/bin/perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter;
our #ISA = qw(Exporter);
our #EXPORT = qw(get_text);
my $author;
my $text_tmp1 =<<'ENG';
This is a template text
by $author.
ENG
sub get_text {
my $tmp = shift #_;
$author = shift #_;
print "In sub author= $author lang = $tmp \n";
my $final_str = eval('$text_'.$tmp);
print "$final_str \n";
return $final_str;
}
1;
Test script:
#!/usr/bin/perl
use strict;
use warnings;
use Modules::TextStuff;
my $str = get_text('tmp1','jim');
print $str;
When I run the test script it does not work. I get:
In sub author=jim lang = eng
Variable "$text_tmp1" is not available at (eval 1) line 2. Use of
uninitialized value $final_str in concatenation (.) or string
How can I fix this?
Combining strings to create variables names is usually a bad idea. You could salvage your current program using our $text_tmp1 = ... instead of my $text_tmp1 = ..., but I think you should consider a different approach, like a hash:
my %templates = (
tmp1 => <<ENG,
This is a template text
by \$author.
ENG
tmp2 => <<ESP,
Esta es templata texta de \$author.
ESP
);
sub get_text {
...
my $final_str = eval( $templates{$tmp} );
...
}
The error you asked about is generated when eval EXPR tries to grab the value of a variable that did exist, but no longer exists.
>perl -wE"{ my $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
Variable "$x" is not available at (eval 1) line 2.
Use of uninitialized value in concatenation (.) or string at -e line 1.
<>
Remember, executing a file (such as a script or a module) is done in its own a lexical scope, just like the one the curlies create above.
It can be fixed by keeping the variable alive by not letting it go out of scope
>perl -wE"my $x = 123; sub f { eval '$x' } say '<'.f().'>';"
<123>
But that's not an option for you.
Other options include making the variable a global variable.
>perl -wE"{ our $x = 123; sub f { eval '$x' } } say '<'.f().'>';"
<123>
Or forcing the sub to capture it so it doesn't cease to exist.
>perl -wE"{ my $x = 123; sub f { $x if 0; eval '$x' } } say '<'.f().'>';"
<123>
(The if 0 silences the "void context" warning.)
That said, it looks like you're trying to re-invent the wheel. Don't invent another half-assed templating system.
I'm looking at several things:
First of all, $text_tmp1 is not a package variable. It's lexically scoped since you declared it with my. If you need it as a package variable and for it to be visible in all or your subroutines, you need to declare it with our.
Your module doesn't compile as written. You are trying to source in $author, but it's not defined.
What are you doing with eval? This is wrong on so many levels.
Here's how I would do it:
#! /usr/bin/env perl
package Modules::TextStuff;
use strict;
use warnings;
use Exporter qw(import);
use Carp;
our #EXPORT_OK = qw(get_text);
our %templates; # This is now a package variable
#
# TEMPLATES
#
$templates{tmp1}=<<TEMPLATE; # We'll use `%s` for replacements
This is a template text
by %s.
TEMPLATE
$templates{tmp2}=<<TEMPLATE;
This is another template and we will substitute
in %s in this one too.
TEMPLATE
sub get_text {
my $template = shift;
my $author = shift;
if ( not exists $templates{$template} ) {
croak qq(Invalid template name "$template");
}
return sprintf $templates{$template}, $author;
}
1;
I'll make each of these templates an entry in my %templates hash. No need for eval to calculate out a variable name for the template. Also notice that I can now actually test whether the user passed in a valid template or not with the exists.
Also note that %template is declared with our and not my. This makes it available in the entire package including any subroutines in my package.
I also use #EXPORT_OK instead of #EXPORT. It's considered more polite. You're requesting permission to pollute the user's namespace. It's like knocking on someone's door and asking if you can have a beer rather than barging in and rummaging through their fridge for a beer.
Note how I use sprintf to handle the replaceable parameters. This again removes the need for eval.
I also prefer to use #! /usr/bin/env perl on my program header since it's more compatible with things like Perlbrew. You're using /usr/bin/env to find the executable Perl program that's in the user's path. This way, you don't have to know whether it's /bin/perl, /usr/bin/perl, /usr/local/bin/perl, or $HOME/perl5/perlbrew/perls/perl-5.18.0/bin/perl
To use your module, I would do this:
#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use Modules::TextStuff qw(get_text);
say get_text('tmp1','jim');
Pretty much the same call you made. This prints out:
This is a template text
by jim.

Using # and $ with the same variable name in Perl

I am declaring the same variable name with # and $:
#ask=(1..9);
$ask="insanity";
print ("Array #ask\n");
print ("Scalar $ask\n");
Without using use strict I am getting output correctly but when I am using use strict it gives me a compilation error.
Do these two variables refer to two different memory locations or is it the same variable?
You've got two variables:
#ask
$ask
You could have %ask (a hash) too if you wanted. Then you'd write:
print $ask, $ask[0], $ask{0};
to reference the scalar, the array and the hash.
Generally, you should avoid this treatment, but the variables are all quite distinct and Perl won't be confused.
The only reason use strict; is complaining is because you don't prefix your variables with my:
#!/usr/bin/env perl
use strict;
use warnings;
my #ask = (1..9);
my $ask = "insanity";
my %ask = ( 0 => 'infinity', infinity => 0 );
print "Array #ask\n";
print "Scalar $ask\n";
print "Hash $ask{0}\n";
with use strict; you need to declare your variables first before using it.
For example:
use strict;
my #ask=(1..9);
my $ask="insanity";
print ("Array #ask\n");
print ("Scalar $ask\n");
#ask and $ask are different variables — as is %ask — and it is not an error to do this. It is however poor style.
Because the sigil changes when you use them, such as when you use $ask[1] to get the second element of #ask, the code becomes harder to read and use strict will also not be able to tell if you've gotten confused. Thus it's a good idea to use names that differ in more than the sigil unless you know what you're doing. So you could use e.g. #asks and $ask.
The error you are getting with strict is not due to variable names. It is because you are not declaring the variables (using one of my, our, local, or state. Nor are you using the vars pragma.
Short answer: Stick a my in front of each variable, and you'll be strict-compliant.
For package variables, you can examine entries in the symbol table. $ask and #ask are separate entities:
#!/usr/bin/env perl
use Devel::Symdump;
use YAML;
#ask=(1..9);
$ask="insanity";
my $st = Devel::Symdump->new('main');
print Dump [ $st->$_ ] for qw(
scalars
arrays
);
Among other things, this code will output:
--
…
- main::ask
…
---
…
- main::ask
…
Being able to use the same name can help when, say, you have an array of fish and you are doing something with each fish in the array:
for my $fish (#fish) {
go($fish);
}
Normally, it is more expressive to use the plural form for arrays and hashes, the singular form for elements of an array, and something based on the singular form for keys in a hash:
#!/usr/bin/env perl
use strict;
use warnings;
my #ships = ('Titanic', 'Costa Concordia');
my %ships = (
'Titanic' => {
maiden_voyage => '10 April 1912',
capacity => 3_327,
},
'Costa Concordia' => {
maiden_voyage => '14 July 2006',
capacity => 4_880,
},
);
for my $ship (#ships) {
print "$ship\n";
}
while (my ($ship_name, $ship_details) = each %ships) {
print "$ship_name capacity: $ship_details->{capacity}\n";
}

Perl built in functions as a subroutine reference

I am trying to a set of operations to be performed as an array. For this, I have to pass sub routine references. (There may be other ways to perform this without using an array. But, I feel this is best for now, due to certain other constraints).
Basic sample code for what I am trying to do:
#!/usr/bin/perl
use strict;
use warnings;
use Data::Dumper;
sub test()
{
print "Tested\n";
}
my $test;
my #temp = (1, 2, 3);
my $operations = [
[\&test, undef, undef],
[\&shift, \$test, \#temp],
];
foreach(#$operations){
my $func = shift $_;
my $out = shift $_;
$$out = $func->(#$_);
}
print Dumper $test;
Output observed is:
Tested
Undefined subroutine &main::shift called at temp2.pl line 22.
Query - Is it possible to pass built in sub routines as a reference?
There are earlier queries already, reg built in functions as a sub routine reference in here.
As the question was asked about 3 years ealier, was wondering if there is any alternative for it now.
Would appreciate if some one explains why there is a distinction between built in functions and user defined sub routines in this scenario?
shift isn't a sub; it's an operator just like and and +. You'll need to create a sub if you want a reference to a sub.
[sub { shift(#{$_[0]}) }, \$test, \#temp],
Related:
What are Perl built-in operators/functions?
How to get a reference to print?

Directly changing an argument to a subroutine

It would make a lot of things easier in my script if I could use subroutines in the way that shift, push, and other built-in subroutines work: they can all directly change the variable that is passed to it without the need to return the change.
When I try to do this the variable is copied at some point and I appear to be simply changing the copy. I understand that this would be fine with references but it even happens with arrays and hashes, where I feel like I am simply passing the variable I was working on to the sub so that more work can be done on it:
#it = (10,11);
changeThis(#it);
print join(" ", #it),"\n"; #prints 10 11 but not 12
sub changeThis{
$_[2] = 12;
}
Is there a way to do this? I understand that it isn't best practice, but in my case it would be very convenient.
That's what prototypes are for:
#!/usr/bin/perl
use strict;
use warnings;
sub changeThis(\#); # the argument will be seen as an array ref (prototype must come before the call!)
my #it = (10,11);
changeThis #it; # even when called with an array
print join(" ", #it),"\n"; #prints 10 11 12
sub changeThis(\#)
{ my( $ar)= #_; $ar->[2]= 12; }
See http://perldoc.perl.org/perlsub.html#Prototypes for more information.
It's not really a popular method though, passing actual array references is probably a better alternative, with less magic involved.
The problem is that the sub call expands the variable to a list of values, which are passed on to the sub routine. I.e. a copy is passed, not the variable itself. Your sub call is equal to:
changeThis(11, 12);
If you wish to change the original array, pass a reference instead:
use strict;
use warnings;
my #it = (10,11);
changeThis(\#it);
print join(" ", #it),"\n";
sub changeThis{
my $array = shift;
$$array[2] = 12;
}
Also, #_[2] will give you the warning:
Scalar value #_[2] better written as $_[2]
If you use warnings, which of course you should. There is no good reason to not turn on warnings and strict, unless you know exactly what you are doing.
As the previous answers suggest, you should use a reference passed to the subroutine.
Additionally you also can use implicit referencing if you want to read trough the documentation for Prototypes
sub changeThis(\#);
#it = (10,11);
changeThis #it;
print join(" ", #it),"\n"; #prints 10 11 12
sub changeThis(\#){
$_[0][2] = 12;
}
(note that you either have to predeclare your subs before the first call or put the sub definitions on top.)