perl OOP pass variable - perl

This is SeqIO.pm
package SeqIO;
use strict;
use Carp;
use warnings;
use vars('#ISA');
use vars('#EXPORT_OK');
require Exporter;
#ISA = qw(Exporter);
#EXPORT_OK = qw(readSeq writeSeq);
sub readSeq {
my ($var1)= #_;
print "$var1\n";
open IN, '<$var1' or die "Cannot open file : $!";
while(<IN>) {
chomp $_;
print "$_\n";
}
close IN
}
sub writeSeq {}
sub new {
my $this = {};
bless $this;
return $this;
}
1;
Test.pl call SeqIO.pm
use strict;
use SeqIO;
use warnings;
my $path_fasta=q/D:\360Downloads\A1.fasta/;
my $seqio = new SeqIO;
$seqio->readSeq($path_fasta);
BUT when i use readSeq it shows SeqIO=HASH(0x38ba34), anything wrong?

When you call a Perl subroutine as a method using the -> operator, the invocant, meaning the thing on the left side of -> is passed into the subroutine as the first parameter. So change your method to this:
sub readSeq {
my ($self, $var1) = #_;
print "$var1\n";
open my $in, '<', $var1 or die "Cannot open file : $!";
while(<$in>) {
chomp $_;
print "$_\n";
}
}
I've also changed your filehandle to a lexical variable instead of a global symbol, and changed your open call to the three-argument version which is better.
There's also no need to export readSeq and writeSeq if you intend to use those as object methods. Exporting is only for when you want to modify the client code's namespace. So you can delete all of this:
use vars('#ISA');
use vars('#EXPORT_OK');
require Exporter;
#ISA = qw(Exporter);
#EXPORT_OK = qw(readSeq writeSeq);

Related

include/eval perl file into unique namespace defined at runtime

I'm writing a tool that must import a number of other perl config files. The files are not wrapped w/packages and may have similar or conflicting variables/functions. I don't have the ability to change the format of these files, so I must work around what they are. What I was thinking to do was import each into a unique name space, but I've not found a way to do that using do, require, or use. If I don't use dynamic names, just a hardcoded name, I can do it.
Want something like this:
sub sourceTheFile {
my ($namespace, $file) = #_;
package $namespace;
do $file;
1;
return;
}
That doesn't work because the package command requires a constant for the name. So then I try something like this:
sub sourceTheFile {
my ($namespace, $file) = #_;
eval "package $namespace;do $file;1;"
return;
}
But the contents of the file read by do are placed in the main:: scope not the one I want. The target scope is created, just not populated by the
do. (I tried require, and just a straight cat $file inside the eval as well.)
I'm using Devel::Symdump to verify that the namespaces are built correctly or not.
example input file:
my $xyz = "some var";
%all_have_this = ( common=>"stuff" );
ADDITIONAL CHALLENGE
Using the answer that does the temp file build and do call, I can make this work dynamically as I require. BUT, big but, how do I now reference the data inside this new namespace? Perl doesn't seem to have the lose ability to build a variable name from a string and use that as the variable.
I am not sure why the eval did not work. Maybe a bug? Here is a workaround using a temp file. This works for me:
use strict;
use warnings;
use Devel::Symdump;
use File::Temp;
my $file = './test.pl';
my $namespace = 'TEST';
{
my $fh = File::Temp->new();
print $fh "package $namespace;\n";
print $fh "do '$file';\n";
print $fh "1;\n";
close $fh;
do $fh->filename;
}
Perl's use and require facilities make use of any hooks you might have installed in #INC. You can simply install a hook which looks in a specific location to load modules with a prefix you choose:
package MyIncHook;
use strict;
use warnings;
use autouse Carp => qw( croak );
use File::Spec::Functions qw( catfile );
sub import {
my ($class, $prefix, $location) = #_;
unshift #INC, _loader_for($prefix, $location);
return;
}
sub _loader_for {
my $prefix = shift;
my $location = shift;
$prefix =~ s{::}{/}g;
return sub {
my $self = shift;
my $wanted = shift;
return unless $wanted =~ /^\Q$prefix/;
my $path = catfile($location, $wanted);
my ($is_done);
open my $fh, '<', $path
or croak "Failed to open '$path' for reading: $!";
my $loader = sub {
if ($is_done) {
close $fh
or croak "Failed to close '$path': $!";
return 0;
}
if (defined (my $line = <$fh>)) {
$_ = $line;
return 1;
}
else {
$_ = "1\n";
$is_done = 1;
return 1;
}
};
(my $package = $wanted) =~ s{/}{::}g;
$package =~ s/[.]pm\z//;
my #ret = (\"package $package;", $loader);
return #ret;
}
}
__PACKAGE__;
__END__
Obviously, modify the construction of $path according to your requirements.
You can use it like this:
#!/usr/bin/env perl
use strict;
use warnings;
use MyIncHook ('My::Namespace', "$ENV{TEMP}/1");
use My::Namespace::Rand;
print $My::Namespace::Rand::settings{WARNING_LEVEL}, "\n";
where $ENV{TEMP}/1/My/Namespace/Rand.pm contains:
%settings = (
WARNING_LEVEL => 'critical',
);
Output:
C:\Temp> perl t.pl
critical
You can, obviously, define your own mapping from made up module names to file names.

How to include a data file with a Perl module?

What is the "proper" way to bundle a required-at-runtime data file with a Perl module, such that the module can read its contents before being used?
A simple example would be this Dictionary module, which needs to read a list of (word,definition) pairs at startup.
package Reference::Dictionary;
# TODO: This is the Dictionary, which needs to be populated from
# data-file BEFORE calling Lookup!
our %Dictionary;
sub new {
my $class = shift;
return bless {}, $class;
}
sub Lookup {
my ($self,$word) = #_;
return $Dictionary{$word};
}
1;
and a driver program, Main.pl:
use Reference::Dictionary;
my $dictionary = new Reference::Dictionary;
print $dictionary->Lookup("aardvark");
Now, my directory structure looks like this:
root/
Main.pl
Reference/
Dictionary.pm
Dictionary.txt
I can't seem to get Dictionary.pm to load Dictionary.txt at startup. I've tried a few methods to get this to work, such as...
Using BEGIN block:
BEGIN {
open(FP, '<', 'Dictionary.txt') or die "Can't open: $!\n";
while (<FP>) {
chomp;
my ($word, $def) = split(/,/);
$Dictionary{$word} = $def;
}
close(FP);
}
No dice: Perl is looking in cwd for Dictionary.txt, which is the path of the main script ("Main.pl"), not the path of the module, so this gives File Not Found.
Using DATA:
BEGIN {
while (<DATA>) {
chomp;
my ($word, $def) = split(/,/);
$Dictionary{$word} = $def;
}
close(DATA);
}
and at end of module
__DATA__
aardvark,an animal which is definitely not an anteater
abacus,an oldschool calculator
...
This too fails because BEGIN executes at compile-time, before DATA is available.
Hard-code the data in the module
our %Dictionary = (
aardvark => 'an animal which is definitely not an anteater',
abacus => 'an oldschool calculator'
...
);
Works, but is decidedly non-maintainable.
Similar question here: How should I distribute data files with Perl modules? but that one deals with modules installed by CPAN, not modules relative to the current script as I'm attempting to do.
There's no need to load the dictionary at BEGIN time. BEGIN time is relative to the file being loaded. When your main.pl says use Dictionary, all the code in Dictionary.pm is compiled and loaded. Put the code to load it early in Dictionary.pm.
package Dictionary;
use strict;
use warnings;
my %Dictionary; # There is no need for a global
while (<DATA>) {
chomp;
my ($word, $def) = split(/,/);
$Dictionary{$word} = $def;
}
You can also load from Dictionary.txt located in the same directory. The trick is you have to provide an absolute path to the file. You can get this from __FILE__ which is the path to the current file (ie. Dictionary.pm).
use File::Basename;
# Get the directory Dictionary.pm is located in.
my $dir = dirname(__FILE__);
open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";
my %Dictionary;
while (<$fh>) {
chomp;
my ($word, $def) = split(/,/);
$Dictionary{$word} = $def;
}
close($fh);
Which should you use? DATA is easier to distribute. A separate, parallel file is easier for non-coders to work on.
Better than loading the whole dictionary when the library is loaded, it is more polite to wait to load it when it's needed.
use File::Basename;
# Load the dictionary from Dictionary.txt
sub _load_dictionary {
my %dictionary;
# Get the directory Dictionary.pm is located in.
my $dir = dirname(__FILE__);
open(my $fh, '<', "$dir/Dictionary.txt") or die "Can't open: $!\n";
while (<$fh>) {
chomp;
my ($word, $def) = split(/,/);
$dictionary{$word} = $def;
}
return \%dictionary;
}
# Get the possibly cached dictionary
my $Dictionary;
sub _get_dictionary {
return $Dictionary ||= _load_dictionary;
}
sub new {
my $class = shift;
my $self = bless {}, $class;
$self->{dictionary} = $self->_get_dictionary;
return $self;
}
sub lookup {
my $self = shift;
my $word = shift;
return $self->{dictionary}{$word};
}
Each object now contains a reference to the shared dictionary (eliminating the need for a global) which is only loaded when an object is created.
I suggest using DATA with INIT instead of BEGIN to ensure that the data is initialised before run time. It also makers it more self-documenting
Or it may be more appropriate to use a UNITCHECK block, which will be executed as early as possible, immediately after the library file is compiled, and so can be considered as an extension of the compilation
package Dictionary;
use strict;
use warnings;
my %dictionary;
UNITCHECK {
while ( <DATA> ) {
chomp;
my ($k, $v) = split /,/;
$dictionary{$k} = $v;
}
}

function call in perl

As a part of my course work I have been learning perl programming language for the first time in last the few weeks. I have been writing small functions and making function calls. I have written a function for string matching.
use strict;
use warnings;
sub find_multi_string {
my ($file, #strings) = #_;
my $fh;
open ($fh, "<$file");
#store the whole file in an array
my #array = <$fh>;
for my $string (#strings) {
if (grep /$string/, #array) {
next;
} else {
die "Cannot find $string in $file";
}
}
return 1;
}
find_multi_string('file name', 'string1','string2','string3','string4','string 5');
In the above script I'm passing the arguments in the function call. The script works.
But I'd like to know if there is way to specify the file name and string1... string n in an array in the program itself and just make the function call.
find_multi_string();
That would be a mistake, always pass parameters and return values to your subroutines.
What you're describing is essentially using subroutines solely to subdivide and document your code. If you were to do that, it would better to just remove the subroutine entirely and include a comment before the section of code.
Overall, your code looks good as is. You probably will want to use quotemeta though, and your logic can be simplified a little:
use strict;
use warnings;
use autodie;
sub find_multi_string {
my ($file, #strings) = #_;
# Load the file
my $data = do {
open my $fh, "<", $file;
local $/;
<$fh>
};
for my $string (#strings) {
if ($data !~ /\Q$string/) {
die "Cannot find $string in $file";
}
}
return 1;
}
find_multi_string('file name', 'string1','string2','string3','string4','string 5');
A few improvements of your original code:
use autodie
use 3-args open
as you want to check anywhere in the file, just load the file as a single string
if the matching string are just text without metacharacters from regexp, just use the index function
Your question is about passing the function arguments from your program.
I suspect that you are looking for #ARGV. See perlvar.
Here is the modified code:
use strict;
use warnings;
use autodie;
sub find_multi_string {
my ($file, #strings) = #_;
my $content = do {
open my $fh, '<', $file;
local $/;
<$fh>
};
foreach (#strings) {
die "Cannot find $string in $file" unless index($content, $_) >= 0;
}
return 1;
}
find_multi_string(#ARGV);

Learning the High Order Perl: issue with iterator

I study the High Order Perl book and have an issue with iterators in the Chapter 4.3.4.
The code:
main_script.pl
#!/perl
use strict;
use warnings;
use FindBin qw($Bin);
use lib $Bin;
use Iterator_Utils qw(:all);
use FlatDB;
my $db = FlatDB->new("$Bin/db.csv") or die "$!";
my $q = $db->query('STATE', 'NY');
while (my $rec = NEXTVAL($q) )
{
print $rec;
}
Iterator_Utils.pm
#!/perl
use strict;
use warnings;
package Iterator_Utils;
use Exporter 'import';;
our #EXPORT_OK = qw(NEXTVAL Iterator
append imap igrep
iterate_function filehandle_iterator list_iterator);
our %EXPORT_TAGS = ('all' => \#EXPORT_OK);
sub NEXTVAL { $_[0]->() }
sub Iterator (&) { return $_[0] }
FlatDB.pm
#!/perl
use strict;
use warnings;
package FlatDB;
my $FIELDSEP = qr/:/;
sub new
{
my $class = shift;
my $file = shift;
open my $fh, "<", $file or return;
chomp(my $schema = <$fh>);
my #field = split $FIELDSEP, $schema;
my %fieldnum = map { uc $field[$_] => $_ } (0..$#field);
bless
{
FH => $fh,
FIELDS => \#field,
FIELDNUM => \%fieldnum,
FIELDSEP => $FIELDSEP
} => $class;
}
use Fcntl ':seek';
sub query
{
my $self = shift;
my ($field, $value) = #_;
my $fieldnum = $self->{FIELDNUM}{uc $field};
return unless defined $fieldnum;
my $fh = $self->{FH};
seek $fh, 0, SEEK_SET;
<$fh>; # discard schema line
return Iterator
{
local $_;
while (<$fh>)
{
chomp;
my #fields = split $self->{FIELDSEP}, $_, -1;
my $fieldval = $fields[$fieldnum];
return $_ if $fieldval eq $value;
}
return;
};
}
db.csv
LASTNAME:FIRSTNAME:CITY:STATE:OWES
Adler:David:New York:NY:157.00
Ashton:Elaine:Boston:MA:0.00
Dominus:Mark:Philadelphia:PA:0.00
Orwant:Jon:Cambridge:MA:26.30
Schwern:Michael:New York:NY:149658.23
Wall:Larry:Mountain View:CA:-372.14
Just as in the book so far, right? However I do not get the output (the strings with Adler and Schwern should occur). The error message is:
Can't use string ("Adler:David:New York:NY:157.00") as a subroutine ref while
"strict refs" in use at N:/Perle/Learn/Iterators/Iterator_Utils.pm line 12, <$fh>
line 3.
What am I doing wrong?
Thanks in advance!
FlatDB calls Iterator, which is defined in Iterator_Utils, so it needs to import that function from Iterator_Utils. If you add
use Iterator_Utils qw(Iterator);
after package FlatDB, the program will work.
Thanks very much for finding this error. I will add this to the errata on the web site. If you would like to be credited by name, please email me your name.

How to keep a variable in scope to all subroutines

i am trying to create a module which is like this
package MyModule;
use strict;
use Exporter;
use vars qw($VERSION #ISA #EXPORT #EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
#ISA = qw(Exporter);
#EXPORT = qw(func1);
sub func1 {
my x = shift;
print x;
func2();
}
sub func2 {
print x;
}
and from a perl script, i am calling func1 of the module and passing a variable x. how do i make that variable visible to both subroutines or say all the functions inside that module.
Please help.
Declare $x in the scope of the file using my or our:
my $x;
# subroutines definition
File has the largest lexical scope, so the variable will be visible for the rest of code (unless you re-declare it in some inner scope using my).
Make $x lexical to the package file rather than a single subroutine:
package MyModule;
use strict;
use Exporter;
use vars qw($VERSION #ISA #EXPORT #EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
#ISA = qw(Exporter);
#EXPORT = qw(func1);
my $x;
sub func1 {
$x = shift;
print $x;
func2();
}
sub func2 {
print $x;
}
But this example doesn't really make sense. A more sensible example would be to define a lexical filehandle that multiple subroutines within the package print to:
package PoorManLogger;
my $fileHandle;
sub initialize { open $fileHandle, '<', +shift }
sub alert { print $fileHandle 'ALERT: ', #_, "\n"; }
sub debug { print $fileHandle 'DEBUG: ', #_, "\n"; }
sub close { close $fileHandle; } # Though this isn't technically needed.
1;
One of the main benefits of OO is encapsulation:
#!/usr/bin/perl
package MyModule;
use strict; use warnings;
sub new {
my $class = shift;
bless { x => shift } => $class;
}
sub x {
my $self = shift;
$self->{x} = shift if #_;
return $self->{x};
}
sub func2 {
my $self = shift;
print $self->x, "\n";
}
package main;
use strict; use warnings;
my $m = MyModule->new(5);
$m->func2;
$m->x(7);
$m->func2;
see our
(comments to my suggestion are correct, my suggestion wasn't)