kiat has asked for the wisdom of the Perl Monks concerning the following question:

Hi, Monks

Greetings!
.
I've a library file which has a subroutine to open a file and assign values from the files to variables. This library file is required (using 'require') by a couple of other files that require the use of those variables. When I declare those variable in the library with 'my', the code doesn't work. Removing 'my' - i.e. making those variables global - solves the problem.
.
What is the normal way to declare variables in a library file that are used by pieces of code in other scripts via 'requre'?
.
I hope my question is clear and I look forward to hearing from you.

kiat

Replies are listed 'Best First'.
Re: Using my and library....
by rinceWind (Monsignor) on Aug 17, 2002 at 09:58 UTC
    What is the normal way to declare variables in a library file that are used by pieces of code in other scripts via 'require'?
    Sharing variables with external code by making them global is considered a bad idea. There are many reasons for this, mainly relating to accidental bugs called side effects, for instance where the same name has been used in two unrelated places.

    The alternative is to make all your subroutines self contained. They receive all the data they need via parameters using @_, and pass information back by returning it either as a single scalar value or as a list. perldoc perlsub goes into this in detail.

    Also, strongly in favour on this site, and in the Perl community in general is a pragma called use strict; which forces declaration of all variables. If you genuinely have global variables (or if you haven't time to fix the code), there are three ways of declaring global variables:

    • Change all $foo to $main::foo. This is explicitly specifying the package name in which $foo resides, and will keep strict happy.

    • use vars qw($foo @bar %baz)

    • our $foo;

      However, in general it's better to make your variables lexical if you can.

    Also worth looking at are perlref and perlreftut

Re: Using my and library....
by Zaxo (Archbishop) on Aug 17, 2002 at 09:42 UTC

    Global variables or lexicals with accessor functions are both possible, but I'd recommend having your subs return a reference to a hash of values keyed by the names you would have given the variables.

    That strategy eliminates the problems presented by globals when more than one copy of your program may run for maintainability and in threaded applications.

    Update: ++smallhotra for spotting the problem in your example, ++tadman for a good inequivalent rewrite. Struck a badly reasoned comment, replaced it by a better one.

    After Compline,
    Zaxo

      Thanks, Zaxo!

      I tried to do what you advised but I seem to be missing something. I've pasted the code here and hope you can point out to me where I have gone wrong:

      # hash.lib # in the library sub gethash { my $hash_ref; open(FILE, "data.txt") || die "Can't open data - $!\n"; # dick:98 # robert:76 # mary:68 chomp(my @lines = <FILE>); close(FILE); foreach (@lines) { my ($name,$score) = split /:/, $_; $hash_ref->{ 'name' } = $name; $hash_ref->{ 'score' } = $score; } return( $hash_ref ); } # script1.pl # some code in another script eval { ($0 =~ m,(.*)/[^/]+,) && unshift (@INC, "$1"); require 5.001; require "hash.lib"; }; my $key_value = gethash(); # I want to be able to print the values here....
      I hope you can enlighten me :)

      kiat
        It's probably better to name your libraries '.pm' instead of '.lib'. Not only can they then be 'use'd properly, but it's really better to conform to existing standards than to invent your own.

        One of the problems with eval is that it can hide errors. Don't forget to check $@ for anything you might have missed.

        Also, in the context of your gethash function you can use an actual hash, and return a reference to it. Saves having to dereference repeatedly. With a tiny bit of tweaking, you get:
        # hash.lib # in the library sub gethash { my %hash; open(FILE, "data.txt") || die "Can't open data - $!\n"; # dick:98 # robert:76 # mary:68 chomp(my @lines = <FILE>); close(FILE); foreach (@lines) { my ($name,$score) = split /:/, $_; $hash{name} = $name; $hash{score} = $score; } return \%hash; }
        More tweaking produces:
        # hash.lib # in the library sub gethash { open(FILE, "data.txt") || die "Can't open data - $!\n"; my %hash = map { (split(/:/))[0,1] } chomp(<FILE>); close(FILE); return \%hash; }
        Always check $@ after an eval.

        A million and one things can go wrong, and you would never know it because eval traps errors. Odds are that you need to add "1;" at the end of your library, and that lack is causing your require to fail, but you didn't get an error message so you didn't get any clues about that.

        From your code, it looks like you are only getting the last entry in your file:
        foreach (@lines) { my ($name,$score) = split /:/, $_; $hash_ref->{ 'name' } = $name; # replaces the old 'name' entry $hash_ref->{ 'score' } = $score; # replaces the previous 'score' + entry }
        I might suggest using an array ref for each entry, or maybe an array of hashrefs or a hash of hashrefs. Examples:
        ##Array of Hashrefs: my @entries; for (@lines) { # as someone once said ~if you plan to explicitly use $_ everywhere, + you might as well give it a more meaningful name or not use it at al +l.~ my ($name,$score) = split /:/; $hash_ref->{ 'name' } = $name; $hash_ref->{ 'score' } = $score; push (@entries, $hash_ref); } return( @entries); } ##Hash of Hashrefs: for (@lines) { my ($name,$score) = split /:/; $hash_ref->{ $name }{'name'} = $name; $hash_ref->{ $name }{'score'} = $score; } return($hash_ref); } ##Hash of Arrayrefs: for (@lines) { my ($name,$score) = split /:/; $hash_ref->{ $name } = [$name,$score]; } return($hash_ref); }
        I could come up with several more data structures to store the same data but you get the idea. TMTOWTDI and as I like to always say, see whatever works best with your program and go with that.