You seem to be confused about
- What the
@
sigil does in Perl, - How character classes in regexes work,
- What context in Perl is
- How the readline operator
<...>
behaves depending on context. - (Why you should never do
&foo
).
The @
sigil
The @
denotes an array. This imposes list context on the right hand side of an assignment.
Character classes
A character class is denoted by square brackets, and can include ranges of characters, e.g. [0-9]
— match the arabic digits. The \d
named character class matches many more characters — whatever Unicode considers "numeric".
A character class can be negated with a leading caret. E.g. [^0-9]
matches any character that is not an arabic digit.
Context
Perl operators behave differently depending on context. For example the readline operator:
- In scalar context (singular), the readline operator reads a single line, but
- in list context (plural), the readline operator reads all lines until the end of file (EOF) is encountered. To send the EOF on keyboard input, you usually use Ctrl-D.
When an @array
is encountered, it evaluates to the list of all elements, in list context. However, in scalar context, it evaluates to the length of the array.
What @get_age = <STDIN>
does
Because the LHS is an array, the RHS is evaluated in list context. As explained above, this reads input until the stream is exhausted. Each element in @get_age
holds one line of the input after the assignment.
What scalar @get_age !~ m/\d[1-99]/
does
This expression is similar to
my $length = scalar @get_age;
not $length =~ /\d[1-9]/;
I.e. the array in scalar context evaluates to the length of the array, which is the number of lines in the input.
The regex says “Inside the string, there must be one unicode numeric character followed by either 1, 2, 3, 4, 5, 6, 7, 8, or 9”. Multiple occurrences of a character in a charclass are discarded. The string may include arbitrary other characters. This should match: "my age is 020!"
What you probably wanted to do:
sub get_age {
print "Age: ";
my $age = <STDIN>; # scalar context, and put into a private variable
chomp $age; # remove newline
if( not length $age ) { # check for empty string
say "You didn't type anything. Retry!";
return get_age();
}
if( $age =~ /[^0-9]/ ) { # recurse on any non-digits
say "You typed non-numeric characters. Restrict yourself to '0' through '9'. Retry!";
return get_age();
}
unless( 1 <= $age && $age <= 99 ) { # check for correct bounds
say "Valid ages have to be in the range 1 to 99. Retry!";
return get_age();
}
return $age;
}
Then in your main code:
my $age = get_age();