Reputation: 1944
I am creating a simple chatterbot program like ELIZA.
I'm taking questions from the terminal and sending reply with dialog, but my program takes only the first input and repeats.
For example, when I run my script the output may be something like this:
[Eliza]: Hi, I'm a psychotherapist. What is your name?
user Input: hello my name is adam.
[Eliza]: hello adam, how are you?
[Eliza]: your name is adam
[Eliza]: your name is adam
[Eliza]: your name is adam
[Eliza]: your name is adam
[Eliza]: your name is adam
And it repeats endlessly.
I don't know where I am doing wrong. How can I get my program to read the next line from the keyboard?
sub hello {
print "[Eliza]: Hi, I'm a psychotherapist. What is your name? \n";
}
sub getPatientName {
my ($reply) = @_;
my @responses = ( "my name is", "i'm", "i am", "my name's" );
foreach my $response ( @responses ) {
if ( lc($reply) =~ /$response/ ) {
return "$'";
}
}
return lc($reply);
}
sub makeQuestion {
my ($patient) = @_;
my %reflections = (
"am" => "are",
"was" => "were",
"i" => "you",
"i'd" => "you would",
"i've" => "you have",
"i'll" => "you will",
"my" => "your",
"are" => "am",
"you've"=> "I have",
"you'll"=> "I will",
"your" => "my",
"yours" => "mine",
"you" => "me",
"me" => "you"
);
if ( $count == 0 ) {
$patientName = getPatientName($patient);
$count += 1;
print "Hello $patientName , How are you? \n";
}
my @toBes = keys %reflections;
foreach my $toBe (@toBes) {
if ($patient =~/$toBe/) {
$patient=~ s/$toBe/$reflections{$toBe}/i;
print "$patient? \n";
}
}
}
sub eliza {
hello();
my $answer = <STDIN>;
while ($answer) {
chomp $answer;
#remove . ! ;
$answer =~ s/[.!,;]/ /;
makeQuestion($answer);
}
}
eliza();
Upvotes: 5
Views: 4973
Reputation: 66873
Your while
loop never reads input. The $answer
got assigned STDIN
before the loop and presumably has a string, that evaluates true in the while
condition. The regex in the loop could in principle change $answer
to false (by removing everything from it, turning it into an empty string)† but it doesn't fetch STDIN
, nor does anything else in the loop body.
Thus not only is no new input assigned to $answer
, but after the first iteration nothing at all changes in the loop.† So it keeps running forever, printing the question based on the same $answer
.
You need
while (my $answer = <STDIN>) {
chomp $answer;
# ...
}
instead.
Every time the condition of while (...)
is evaluated the new input is read via <STDIN>
and is assigned to $answer
. Then each new question uses the new $answer
. Note how a variable may be declared inside the while
condition so to exist only inside the loop body (and in the condition after its declaration). This is a nice way to keep its scope restricted to where it is needed, inside the loop.
The filehandle read <...>
returns undef
when it gets EOF
(or on error) and the loop terminates. See I/O Operators in perlop. A user at the terminal can normally achieve this by Ctrl-d
.
† For example, a regex like $answer =~ s/.//;
removes a character each time it runs (except a newline, which .
doesn't match without the /s
modifier), so if applied repeatedly eventually $answer
winds up being an empty string. Of course, the regex in the question doesn't do that but it changes the string only the first time it runs on it.
Upvotes: 8