http://qs321.pair.com?node_id=833168

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

Somewhere in the following script, a variable is not being undefined causing the loop to display too much. I have been trying various ways to undefine the variables that I think are causing the problem without success. I thought the module was working fine, since I tested it before putting it into use, but not in a loop. I am very stumped. Any help would be appreciated.

I have spent over an hour trying to fix this. I am sorry it is so long. If you see any other issues, please let me know. Thank you in advance and have a cookie while you are reading.

Classes.pm

The problem could be that the variables %classes or @classes is not being undefined when the subroutines finish. I have tried undefining them both with no success at the beginnings and ends of each of the class_data and print_classes subroutines.

package RolePlaying::Classes; use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = qw(class_data print_classes return_THAC0 return_famil +iarity return_non_proficiency); use lib "C:/Documents and Settings/Dawn M. Burge/My Documents/fantasy/ +files/perl/lib"; use Lingua::EN::Inflect qw(ORD); use List::Util qw(max min); use Tie::IxHash; use RolePlaying::Level qw(get_level); use RolePlaying::THAC0 qw(get_THAC0 get_familiarity get_non_proficienc +y); tie my %classes, qw(Tie::IxHash); my @classes; my @THAC0; my @familiarity; my @non_proficiency; sub class_data { my ($experience,@values) = @_; for my $class (@values) { my @class_array = split(m!\/!,$class); my $key = $class_array[0]; if (@class_array == 3) { $classes{$key}{xp_class} = $class_array[1]; $classes{$key}{class_type} = $class_array[2]; } elsif (@class_array == 2) { $classes{$key}{xp_class} = $class_array[1]; $classes{$key}{class_type} = $class_array[1]; } else { $classes{$key}{xp_class} = $class_array[0]; $classes{$key}{class_type} = $class_array[0]; } $classes{$key}{level} = get_level($classes{$key}{xp_class},$experi +ence); push @THAC0, get_THAC0($classes{$key}{level},$classes{$key}{class_ +type}); push @familiarity, get_familiarity($classes{$key}{class_type}); push @non_proficiency, get_non_proficiency($classes{$key}{class_ty +pe}); } } sub print_classes { my ($experience,@data) = @_; class_data($experience,@data); for my $key (keys %classes) { push @classes, ORD($classes{$key}{level}).' level '.$key; } return join(' / ',@classes); } sub return_THAC0 { my ($experience,@data) = @_; class_data($experience,@data); return min(@THAC0); } sub return_familiarity { my ($experience,@data) = @_; class_data($experience,@data); return min(@familiarity); } sub return_non_proficiency { my ($experience,@data) = @_; class_data($experience,@data); return min(@non_proficiency); } 1;

The loop in chracters.pl

I have also tried to undefine @clsses in the loop. However, like above, no success. The oddity is that $name and $experience do not have the same problem. They stay, I think the word is, scoped.

for my $name (keys %characters) { my $id = $name; $id =~ s! !_!g; $id =~ s![^-\w:.]!!g; my $experience = $characters{$name}{experience}; my @classes = @{$characters{$name}{classes}}; print qq{<h1 id="$id">$name</h1>\n}; print qq{\t<p>}.print_classes($experience,@classes).qq{</p>\n}; print qq{\t<p>Experience: $experience</p>\n}; }

Sample data

tie my %characters, qw(Tie::IxHash); %characters = ( "Awnday Ar'iemay" => { experience => 250000, classes => ["thief/rogue","mage/wizard"], }, "Amelda Bacoulan" => { experience => 8800, classes => ["psionisist"], }, "Sarria Bagada" => { experience => 4555, classes => ["priestess of Torm/specialty_priest/priest"], }, "Verago Brystar" => { experience => 94150, classes => ["priestess of Mystra/specialty_priest/priest","mage/wi +zard"], }, "Matea Fi'nashari" => { experience => 2500, classes => ["fighter/warrior","thief/rogue","mage/wizard"], }, );
Have a nice day!
Lady Aleena
  • Comment on Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
  • Select or Download Code

Replies are listed 'Best First'.
Re: Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
by TGI (Parson) on Apr 07, 2010 at 05:59 UTC

    How's this for a totally offtopic response? The snippet of code you show screams "use objects" to me. Really really loudly.

    I mean, you have a "Character" that has_some "Classes" and has 'experience' and 'name' and all sorts of other attributes.

    You have classes that have experience requirements and various saving throw and THAC0 values by level.

    In short, you have big nested data structures with associated behaviors. Using OOP design will help corral the various related stuff into smaller, easier to swallow chunks.

    OK, on to the answer to your problems. You have several file scoped lexicals @classes, @THAC0 among them. You push data into them and never clear it out. So if you call print_classes() multiple times, you will keep extending the @classes array forever.

    In general, keep all variables in the tightest possible scope and be very wary of global variables (and file scoped lexicals, which are almost the same). For proof of this maxim, if you move the declaration of @classes into the print_classes() routine, your problem will vanish.

    When you design a program, one thing you can do to keep things simple is to avoid side-effects in your functions. What this means is that for a given input, you get a given output every time, and nothing else about the program changes.That way, 6 months from now, when your code has become an indispensable tool, and you find a bug, you won't have to remember that foo() must be called before bar() but after whiz(), because they all interact with %whibble, and alter its value.

    The OOP stuff helps with this. By tying behaviors to data in neat chunks it makes it easier to partition problems into models that work, while avoiding promiscuous data sharing that leads to spaghetti code.

    This looks like a fun project. Happy hacking.


    TGI says moo

Re: Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
by 7stud (Deacon) on Apr 07, 2010 at 04:01 UTC

    have also tried to undefine @clsses in the loop. However, like above, no success.

    for my $name (keys %characters) { my $id = $name; $id =~ s! !_!g; $id =~ s![^-\w:.]!!g; my $experience = $characters{$name}{experience}; my @classes = @{$characters{$name}{classes}};

    You are declaring @classes inside the loop. Therefore, every time through the loop, the @classes variable is destroyed. A new @classes variable is created each time through the loop and it is assigned a new value.

    It's not clear to me why you want to 'undefine' @classes. Is it possible you are doing something like this:

    use strict; use warnings; use 5.010; my @classes = ('A', 'B'); for (1 .. 3) { my @classes = ('C', 'D'); say "@classes"; } say "\n@classes"; --output:-- C D C D C D A B

    Anything you do to @classes inside the loop braces only affects the @classes variable that you declared inside the loop braces. Changes to the @classes variable inside the loop braces aren't going to affect the @classes variable declared outside the loop braces.

      No, I think the OP is referring to @classes and %classes in the first file, "Classes.pm". In that package, @classes and %classes are being used as global shared state for the various functions. There, @classes and %classes need to be reset before each new call to class_data, or else the data from separate calls will commingle.

        amedico, 7stud read correctly, I was trying to undefine @classes in the for loop too. :S

        Have a nice day!
        Lady Aleena
      every time through the loop, the @classes variable is destroyed

      I thought as much, so the problem is with Classes.pm somewhere. For some reason (%|@)classes is retaining data from previous loops. Every effort I have made to undefined both of those values has failed. I may need a whole new approach.

      Have a nice day!
      Lady Aleena
Re: Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
by chuckbutler (Monsignor) on Apr 07, 2010 at 04:19 UTC

    LA:

    @classes and %classes are declared at the top lex level of the package, so they with not go out of scope until the program exits. When it is appropriate, within a routine in the package, you can:

    ... @classes = (); %classes = (); ...

    to empty them.

    Good luck. -c

Re: Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
by ikegami (Patriarch) on Apr 07, 2010 at 06:26 UTC
    By the way, use lib doesn't make any sense in a module. Perl can obviously already find the modules if it encounters the line.
      use lib doesn't make any sense in a module.

      Why not?  Would make sense, for example, if the module uses other modules from (other) non-standard library locations...

      (I don't know where the modules reside in the OP's case, so I can't tell whether it makes sense here, though.)

        FWIW, I think use lib is for programs

      Actually, it can't since I am not using the standard module libraries for my really really site specific modules. I am storing them elsewhere. I have gone without the use lib line when using modules in the same directory, and I get all kinds of errors. There is no chance that I can get any provider to add that folder to the module library, as far as I know. And honestly, who would want them other than me?

      Have a nice day!
      Lady Aleena

        The module in which you use use lib is in that non-standard location, right? So why do you say you need to tell Perl where to look.

        You just need the use lib in the script. The use lib in the module is redundant.

Re: Problem undefining a variable in a loop using a module causing a list to not empty after use of a subroutine
by Khen1950fx (Canon) on Apr 07, 2010 at 06:10 UTC
    I've concentrated on the characters.pl script. This may or may not help, but I'm not getting the errors that I was getting when I first ran it:
    #!/usr/bin/perl use strict; use warnings; use diagnostics; foreach my $name (keys my %characters) { my $id = $name; $id =~ s/ /_/g; $id =~ s/[^-\w:.]//g; my $experience = $characters{$name}{'experience'}; my(@classes) = @{$characters{$name}{'classes'};}; print qq[<h1 id="$id">$name</h1>\n]; print "\t<p>" . print_classes($experience, @classes) . "</p>\n"; print "\t<p>Experience: $experience</p>\n"; }

      I am sorry to say that I can not see the difference other than you putting @classes in parentheses, but it may be that I am tired. Though on the same line there are two semi-colons, one before the closing bracket and one after. Did you want that semi-colon there?

      Have a nice day!
      Lady Aleena