There is a logic error in your code. For purposes of demonstration I'll switch from using STDIN
as your input handle, in its place using DATA
so that I can bake-in some test data. I'll also eliminate the outer subroutine, as it's not pertinent to the discussion.
use strict;
use warnings;
my %profession = (
Emelie => 'Economist',
Hugo => 'Scientist',
Maria => 'Accountant',
Linnéa => 'Medical Doctor',
);
chomp(my $name = <DATA>);
my $var = 1;
while($var) {
if (exists $profession{$name}) {
print "The profession for $name is $profession{$name}\n";
$var = 0; # This could just be the 'last' keyword.
}
else {
print "No such name ($name) found. Try again.\n";
}
}
__DATA__
John
Dave
Maria
As you can see from the sample data you would expect a program with correct logic to terminate after the third try, because Maria
is one of your hash keys. But if you run it you see this:
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
^C
Command terminated
(The ^C
and Command terminated
lines are coming from when I finally hit -c to terminate the run.)
At this point, with the additional information of printing the name that was most recently read from <DATA>
it's pretty easy to see that we are only ever looking at John
. But why? Because your loop doesn't read from the <DATA>
filehandle. You're doing that outside the loop.
Here's a more Perlish way to accomplish what you're wanting to do:
use strict;
use warnings;
my %profession = (
'Emelie' => 'Economist',
'Hugo' => 'Scientist',
'Maria' => 'Accountant',
'Linnéa' => 'Medical Doctor',
);
while(my $name = <DATA>) {
chomp $name;
if (exists $profession{$name}) {
print "The profession for $name is $profession{$name}\n";
last;
}
print "No such name ($name) found. Try again.\n";
}
__DATA__
John
Dave
Maria
Now the output will be:
No such name (John) found. Try again.
No such name (Dave) found. Try again.
The profession for Maria is Accountant
The first time through this while
loop we read from DATA
and obtain John\n
. We assign it to $name
, chomp
it, and then check to see if John
exists as a hash key. It doesn't, so we print the name and move on to the next iteration. On the second iteration <DATA>
reads Dave\n
, chomps it, and checks if it exists. It doesn't, so we print the name and move on to the next iteration.
On the third iteration we <DATA>
obtains Maria\n
, chomps it, and checks if a hash key Maria
exists. It does, so it prints the value associated with that key, and then hits the last
statement. last
tells the control of flow to exit the enclosing loop immediately. The remainder of the lines within the main block of the loop are skipped, and there are no more iterations. It is often more legible than a sentinel variable such as $var
in your example code, as there's no need for the reader of the code to keep track of what state a variable may be in.
So in short, your mistake was only reading from the input filehandle one time, and then expecting your loop to encounter changes in $name
despite it only being assigned to one time, before entering the loop. The solution was to move the file read to become part of the loop.
This pattern is documented in perldoc perlvar
, which I encourage you to spend a few minutes reading over to gain a better familiarity with the language.
Update: I do see an answer where calling main()
again is used as a means of iterating over the file read, and this is not wrong. But in a real script main()
is likely to grow larger, and when that happens the file read loop would either need to become an explicit loop again, or be broken out to a different subroutine that can call itself. Additionally, the while
loop approach is an idiomatic or common solutions, more likely to be found in code written by others. Using recursion to read a file is a less common pattern.