Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Problem Declaring Variable

by PrimeLord (Pilgrim)
on Mar 13, 2002 at 16:05 UTC ( [id://151415]=perlquestion: print w/replies, xml ) Need Help??

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

Monks I am seeking your help once again. I apologize in advance if I am just over looking something very simple, but I just can't figure out what I am doing wrong here. I am working on a script that first reads some data into a hash. Then it reads some data from a flat txt benchmark file into another hash. From there I am printing a report that essentially just compares the hashes to print the data. However I am getting an error that one of my variables needs to an explicit package name and I can;t figure out why. Here is the code. I have tried to strip it down to just the pertinent stuff.
#!/usr/local/bin/perl -w use strict; use vars qw($opt_m); use Getopt::Std; my ($today) = &finder(); if ($script_mode eq 'normal') { my ($yesterday) = &read_benchmark(); &write_benchmark(); &print_report(); } else { &write_benchmark(); } sub finder { my %today; open (IN, "< todays data") or die "$!"; while (<IN>) { chomp; $today{$_}++; } close (IN); return \%today; } sub read_benchmark { my %yesterday; open (BENCH, "< benchmark.txt") or die "$!"; while (<BENCH>) { chomp; $yesterday{$_}++; } close (BENCH); return \%yesterday; } sub write_benchmark { open (BENCH, "> benchmark.txt") or die "$!"; foreach (sort keys %$today) { print BENCH "$_\n"; } close (BENCH); chmod(0640, "benchmark.txt") or die "$!"; return; } sub print_report { foreach (sort keys %$today) { print LOG "$_\n" unless exists $yesterday->{$_}; } foreach (sort keys %$yesterday) { print LOG "$_\n" unless exists $today->{$_}; } print LOG "End of Report.\n"; return; }
The Error message I get is. Global symbol "$yesterday" requires explicit package name at ./daily10 line 115. Line 115 is where I try to use $yesterday->{$_}. Now I am guessing that %yesterday isn't accessible because it is returned to that if statement, but the other subs that access are within that if statement too. I guess by declaring the variable in use vars that would probably work, but I don't understand why it isn;t working here. Any help is appreciated. Thanks.

Replies are listed 'Best First'.
Re: Problem Declaring Variable
by ChOas (Curate) on Mar 13, 2002 at 16:07 UTC
    Try:
    &print_report($yesterday); #And in print_report: sub print_report { my $yesterday=shift foreach (sort keys %$today) { print LOG "$_\n" unless exists $yesterday->{$_}; } # etc. etc. etc.


    GreetZ!,
      ChOas

    print "profeth still\n" if /bird|devil/;

      Exactly! ++ChOas.

      Rather than effectively creating a global variable by moving the declaration of my $yesterday out of the if ($script_mode eq 'normal') { statement in the main code (as has been suggested in other comments), it is much better to pass it as a parameter to the print_report subroutine.

      In fact, I'd even take it one step further by also passing $today to all three subroutines that use it (read_benchmark, write_benchmark AND print_report), thus decoupling those subroutines from this specific application and making them candidates for a Perl module (e.g., if the scope of the program were to grow, as occasionally happens, requiring the writing of other scripts which would otherwise have to use cut'n'pasted copies of the same routines)./P>

      dmm

      If you GIVE a man a fish you feed him for a day
      But,
      TEACH him to fish and you feed him for a lifetime
Re: Problem Declaring Variable
by dragonchild (Archbishop) on Mar 13, 2002 at 16:13 UTC
    First, I'd like to commend you for trying to figure out how to use strict. And, yes, the problem is one of scoping.

    There are two solutions, one better than the other.

    1. Put a my $yesterday; outside of the if clause. Then, it's just $yesterday = &read_benchmark();. This is creating a global variable. Acceptable, but not very scalable.
    2. The better solution is to pass $yesterday into print_report(). You'd do it something like this:
      my $yesterday = &read_benchmark(); ... &print_report($yesterday); ... sub print_report { my ($yesterday) = @_; # Rest of function the same. }
      This is considered better because you could then re-use the print_report() function with some other script and it wouldn't depend on $yesterday being declared for it.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.

        definetly pass $yesterday to avoid demerphq's unintended global

        Well, if you have subs at the bottom and main code outside of a block, like this author has, ALL lexicals that aren't in a block, are semi-global. So you can avoid having this one as an "inintended global" by putting it in the if-block, but that still leaves all unblocked lexicals. In this stripped piece of code, that's only $today, but in larger programs the results can be disastrous.

        U28geW91IGNhbiBhbGwgcm90MTMgY
        W5kIHBhY2soKS4gQnV0IGRvIHlvdS
        ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
        geW91IHNlZSBpdD8gIC0tIEp1ZXJk
        

      1. Put a my $yesterday; outside of the if clause. Then, it's just $yesterday = &read_benchmark();. This is creating a global variable. Acceptable, but not very scalable.

      That would issue a compile time error. You need to tell you're going to use the global first, with our or use vars. This can be avoided by just using my: the lexical scopes lasts till the end of the file, so the subs can access it. This behaviour is often unintended, and the results are indeed not very scalable. Because of that, I advise the author of the root node in this thread to put main code after the subs, to get them out of lexical scope.

      Update - It would not issue a compile time error, because it would not be a global at all. Instead, it'd be a lexical variable that lasts until the end of the file. This makes most of my original post untrue. However, it is imho still a bad idea to use globally used lexicals.

      U28geW91IGNhbiBhbGwgcm90MTMgY
      W5kIHBhY2soKS4gQnV0IGRvIHlvdS
      ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
      geW91IHNlZSBpdD8gIC0tIEp1ZXJk
      

Re: Problem Declaring Variable
by YuckFoo (Abbot) on Mar 13, 2002 at 16:19 UTC
    I think you should pass $today and $yesterday to the print_report subroutine: print_report($today, $yesterday);

    The same should be done with write_benchmark, you should pass the hash reference to the sub instead of relying on a global variable: write_benchmark($today);

    Your programs will be more readable, maintainable, and scalable.

    Inside print_report, access them like this:

    sub print_report { my ($today, $yesterday) = @_; }
Re: Problem Declaring Variable
by PrimeLord (Pilgrim) on Mar 13, 2002 at 16:22 UTC
    That took care of the problem. Thanks guys!!!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://151415]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others contemplating the Monastery: (6)
As of 2024-04-19 14:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found