Perl Weekly Challenge 287: Valid Number
These are some answers to the Week 287, Task 2, of the Perl Weekly Challenge organized by Mohammad S. Anwar.
Spoiler Alert: This weekly challenge deadline is due in a few days from now (on September 22, 2024, at 23:59). This blog post provides some solutions to this challenge. Please don’t read on if you intend to complete the challenge on your own.
Task 2: Valid Number
You are given a string, $str
.
Write a script to find if it is a valid number.
Conditions for a valid number:
- An integer number followed by an optional exponent.
- A decimal number followed by an optional exponent.
- An integer number is defined with an optional sign '-' or '+' followed by digits.
Decimal Number:
A decimal number is defined with an optional sign '-' or '+' followed by one of the following definitions: - Digits followed by a dot '.'. - Digits followed by a dot '.' followed by digits. - A dot '.' followed by digits.
Exponent:
An exponent is defined with an exponent notation 'e' or 'E' followed by an integer number.
Example 1
Input: $str = "1"
Output: true
Example 2
Input: $str = "a"
Output: false
Example 3
Input: $str = "."
Output: false
Example 4
Input: $str = "1.2e4.2"
Output: false
Example 5
Input: $str = "-1."
Output: true
Example 6
Input: $str = "+1E-8"
Output: true
Example 7
Input: $str = ".44"
Output: true
Valid Number in Raku
We could implement a regex one-liner, something like this:
/<[+-]> (\d+\.? \d*) | (\. \d+) ...
However, I find it more convenient and more maintainable to use a relatively advanced feature of Raku regexes, named captures, or, even better, named regexes, which come in three flavors: regexes, tokens and rules. They are the main ingredient for grammars, but it is convenient to use them as building blocks, even when implementing a full-fledged grammar might be technological overkill. The syntax for defining them is similar to a subroutine or method definition, for example:
my token sign { <[+-]> }
Once you have defined the sign
token as above, you can use it with the <sign>
name.
The program below shows multiple uses of named regexes as building blocks for more complicated ones.
sub valid-number ($in) {
my token sign { <[+-]> }
my regex integer { <sign>? \d+ }
my token exponent { <[eE]> <integer> }
my token decimal {
<integer> '.'? | <sign>? '.'? \d+ | <integer> '.' \d+
}
my regex float { <decimal> <exponent> }
my token number { <float> | (<decimal> <exponent>?) }
return so $in ~~ /^ <number> $/ ;
}
for <1 a . 1.2e4.2 -1 +1E-8 .44 16 12.5 5e17e3 foo> -> $test {
printf "%-10s => ", $test;
say valid-number $test;
}
This program displays the following output:
$ raku ./valid-number.raku
1 => True
a => False
. => False
1.2e4.2 => False
-1 => True
+1E-8 => True
.44 => True
16 => True
12.5 => True
5e17e3 => False
foo => False
Valid Number in Perl
Perl doesn't have named regexes
, so we will use standard Perl regexes, with the /x
option to enable spaces and comments within the regex.
use strict;
use warnings;
use feature 'say';
sub valid_number {
my $in = shift;
return "True" if $in =~
/^[+-]? # start of string & optional + or - sign
( \d+\.? # digit(s) followed by a dot
| \d*\.\d+) # or digits with a dot inside or before
([eE][+-]\d+)? # optional positive or negative exponent
$/x; # end of string & option to enable comments
return "False";
}
my @tests = qw<1 a . 1.2e4.2 -1 +1E-8 .44 16 12.5 5e17e3 foo>;
for my $test (@tests) {
printf "%-10s => ", $test;
say valid_number $test;
}
This program displays the following output:
$ perl valid-number.pl
1 => True
a => False
. => False
1.2e4.2 => False
-1 => True
+1E-8 => True
.44 => True
16 => True
12.5 => True
5e17e3 => False
foo => False
Wrapping up
The next week Perl Weekly Challenge will start soon. If you want to participate in this challenge, please check https://perlweeklychallenge.org/ and make sure you answer the challenge before 23:59 BST (British summer time) on September 29, 2024. And, please, also spread the word about the Perl Weekly Challenge if you can.
Leave a comment