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.
 I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.
	            I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.
Leave a comment