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

ive had this markov chain irc bot for a long time, and have recently been hacking on it. the thing is, it wont run with 'use strict/warnings' for very long. i cant figure out why, all the nested data structures are driving me insane. can anyone tell me why iy gives me errors (especially at lines 244, 208, 249)? id be very grateful! code:
#!/usr/bin/perl use warnings; use strict; use Storable; use Data::Dumper; use Net::IRC; my $mynick = "wabbylegs"; my $ident = "quetzal"; my $server = "irc.whackpack.com"; my $channel = "#"; my $public = 1; my $learn = 1; my @words = ({}); my %wordhash = (); my $sleeptime = 5; my $irc = new Net::IRC; my $connection = $irc->newconn(Nick => $mynick, Username => $ident, Server => $server, Port => "6667", Ircname => "dont whois me" ) or die "Cant connect to $server.\n"; my %commands = (savedict => \&savedict, loaddict => \&loaddict, die => \&die, quiet => \&quiet, loud => \&loud, stats => \&stats, dumpdict => \&dumpdict, ); $connection->add_handler('376', \&on_connect); # end of motd $connection->add_handler('public', \&on_public); $connection->add_handler('msg', \&on_private); $connection->add_global_handler('disconnect', \&on_disconnect); $irc->start; ## ### Subroutines. ## sub on_connect() { my $self = shift; $self->join($channel); } sub on_public { my($self, $event) = @_; my($text) = ($event->args); my $from = $event->nick; $text = lc $text; if($sleeptime <= 0) { if($public) { $self->privmsg($channel, spew()); } $sleeptime = sprintf "%d", (rand 2) + 5; } $sleeptime--; print "Msgs to spew: $sleeptime\n"; print "msg: $text\n"; if($text =~ /^$mynick: (\w+)/) { my $cmd = $commands{$1}; print "Got command: $1 ($cmd)\n"; if(defined($cmd)) { &$cmd($self, $event, $from, $channel); } } else { if ($learn) { add_sentence($text); } } } sub on_private { my $i = 0; my($self, $event) = @_; my($text) = ($event->args); my $pnick = $event->nick; $text =~ /^(\w+)/; if(defined($commands{$1})) { my $cmd = $commands{$1}; print "Got private command: $1 ($cmd)\n"; &$cmd($self, $event, $pnick, $pnick); } else { $self->privmsg($pnick, spew()); } } sub savedict($$$$) { my ($self, $event, $nick, $chan) = @_; store \@words, "mydict"; $self->privmsg($chan, "Saved " . scalar @words . " words to mydict +."); } sub loaddict() { my ($self, $event, $nick, $chan) = @_; my $w = retrieve "mydict"; @words = @{$w}; $self->privmsg($chan, "Loaded ".scalar @words." from mydict."); } sub die() { my ($self, $event, $nick, $chan) = @_; $nick = $event->{nick}; print "got die from $nick in $chan\n"; if($nick eq "nick") { $self->privmsg($channel, "$nick killed me!!"); $self->quit("$nick killed me."); exit 0; } else { $self->privmsg($nick, "I won't die unless nick tells me.\n"); return 0; } } sub stats() { my ($self, $event, $nick, $chan) = @_; my $nwords = scalar @words; my $average = 0; foreach(@words) { $average += scalar @{$_->{num}}; } $average /= $nwords; $average = sprintf "%.2f", $average; my $stats = "I know $nwords words, with an average of $average con +nections between each word. I am waiting for $sleeptime mo$ $self->privmsg($chan, $stats); } sub quiet() { my ($self, $event, $nick, $chan) = @_; $public = 0; $self->privmsg($chan, "Shutting up, $nick."); } sub loud() { my ($self, $event, $nick, $chan) = @_; $public = 1; $self->privmsg($chan, "I'll speak now, $nick."); } sub dumpdict() { my ($self, $event, $nick, $chan) = @_; my $i = 0; open FILE, ">textdict"; foreach(@words) { printf FILE "%4d Word: $_->{word} ", $i; print FILE join "|", @{$_->{num}}; print FILE "\n"; $i++; } close FILE; } sub spew() { my $rnd = sprintf "%d", rand scalar @{$words[0]->{num}}; my $start = @{$words[0]->{num}}[$rnd-1]; my $s = "\u$words[$start]->{word}"; my $next = $start; while($next != -1) { $rnd = sprintf "%d", rand scalar @{$words[$next]->{num}}; $next = @{$words[$next]->{num}}[$rnd]; if($next != -1) { $s .= " $words[$next]->{word}"; } } $s .= "."; $s =~ s/ /, /g; return $s; } sub add_sentence() { my $string = lc shift; my @s = split "[.,!?]", $string; foreach(@s) { s/[^a-z-A-Z0-9 \t']+//g; my @w = split " "; my $next_push = -1; my $old_index = 0; foreach(@w) { my $index = scalar(@words); my $i = find_word($_); my $new_index = ($i == -1)?$index:$i; if($i == -1) { $words[$new_index] = {word => $_}; } else { } push @{$words[$old_index]->{num}}, $new_index; $string =~ /^(\w+)/; if($1 eq $_) { push @{$words[0]->{num}}, $index; } $string =~ /(\w+)$/; if($1 eq $_) { push @{$words[$new_index]->{num}}, -1; } $old_index = $new_index; } } } sub find_word() { my $word = shift; if(defined $wordhash{$word}) { return $wordhash{$word}; } for(my $i = 0; $i < scalar(@words); $i++) { if($words[$i]->{word} eq $word) { $wordhash{$word} = $i; return $i; } } return -1; } sub on_disconnect { my ($self, $event) = @_; print "Disconnected from ", $event->from(), " (", ($event->args()) +[0], "). Attempting to reconnect...\n"; $self->connect(); }

Replies are listed 'Best First'.
Re: wont run for long with strict.
by foogod (Friar) on Aug 12, 2001 at 08:59 UTC

    Well I can't see any problems on those lines that you mention. The only error I see is on line 146. You are not ending your statement. (This may just be a copy and past problem.)

    Also I have been running it for a few hours and it has been fine, my best advice would be to check your /var/logs/ directory and see if it is writing a log entry when it fails. (It probably is)

    How long does it run before it fails? If it is months ... then maybe there is a memory problem. What kind of system does this run on?

    All in all, need more info to find the problem. The code is syntacically (sp?) ok.

    - f o o g o d

Re: wont run for long with strict.
by tachyon (Chancellor) on Aug 12, 2001 at 09:58 UTC

    In my editor the only error is at line 144 where the string is not terminated. Line 208 is blank; line 244 is a } and line 249 is sub on_disconnect. None of these seem likely to be the source of your problems so somehow the lines are out of sync. Exactly what errors do you get and what are the lines? Correcting the syntax error it runs under strict and warnings for me.

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: wont run for long with strict.
by rchiav (Deacon) on Aug 12, 2001 at 07:52 UTC
    What are the specific errors (usually helpful) and which lines are 244, 208 and 249. I don't think it's reasonable to expect people to try to count down 200+ lines to possibly be incorrect about which lines you're talking about..

    Rich

      heh oops. sorry.
      errors:
      
      when i give the bot input in the channel, ill get lots of this:
      
      Use of uninitialized value in string eq at ./wabbylegs line 244.
      Use of uninitialized value in string eq at ./wabbylegs line 249.
      Use of uninitialized value in string eq at ./wabbylegs line 244.
      Use of uninitialized value in string eq at ./wabbylegs line 249.
      Use of uninitialized value in string eq at ./wabbylegs line 244.
      Use of uninitialized value in string eq at ./wabbylegs line 249.
      Use of uninitialized value in string eq at ./wabbylegs line 244.
      Use of uninitialized value in string eq at ./wabbylegs line 249.
      
      and then, he'll crash with this line after a certain amount of lines:
      
      
      Can't use an undefined value as an ARRAY reference at ./wabbylegs line 208.
      
      
        Well If I'm guessing correctly, these line numbers are off by 25 from what you've posted here. the lines in question are 183, 219 and 224 (in the script that you posted).

        First, is this is exact script that you're running? It doesn't make sense that it's off by 25 lines.

        Second, I'm not going to run this script, but the errors you're getting are all related to the @words array. It looks like this array gets populated by reading a file from disk. Does this file exist? If so, there might be some bad entries in there that aren't being checked for properly. As for the uninitialized values in the "eq" statements, I'm guessing that your regex isn't matching anything and $1 isn't being populated. You should check for a match and not assume that you have one. Just expand your regex to be an if statement, only executing the following code if there's a match.

        And for any who want to look at this and provide information, here's the lines in question..(I think)

        .................................... sub spew() { my $rnd = sprintf "%d", rand scalar @{$words[0]->{num}}; my $start = @{$words[0]->{num}}[$rnd-1]; my $s = "\u$words[$start]->{word}"; my $next = $start; while($next != -1) { # THIS NEXT LINE GENERATES THE ARRAY REF ERROR (I THINK) - rich $rnd = sprintf "%d", rand scalar @{$words[$next]->{num}}; $next = @{$words[$next]->{num}}[$rnd]; if($next != -1) { $s .= " $words[$next]->{word}"; } } $s .= "."; $s =~ s/ /, /g; return $s; } ................................... sub add_sentence() { my $string = lc shift; my @s = split "[.,!?]", $string; foreach(@s) { s/[^a-z-A-Z0-9 \t']+//g; my @w = split " "; my $next_push = -1; my $old_index = 0; #WON'T THIS STOMP ON $_ FROM THE foreach(@s)? foreach(@w) { my $index = scalar(@words); my $i = find_word($_); my $new_index = ($i == -1)?$index:$i; if($i == -1) { $words[$new_index] = {word => $_}; } else { #WHY IS THIS EMPTY? -rich } push @{$words[$old_index]->{num}}, $new_index; $string =~ /^(\w+)/; #THE FIRST UNINIT VALUE ERROR - rich if($1 eq $_) { push @{$words[0]->{num}}, $index; } $string =~ /(\w+)$/; #THE SECOND UNINIT VALUE ERROR - rich if($1 eq $_) { push @{$words[$new_index]->{num}}, -1; } $old_index = $new_index; } } }
        Rich