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

About laurent_r

user-pic I am the author of the "Think Perl 6" book (O'Reilly, 2017) and I blog about the Perl 5 and Raku programming languages.