File Coverage

blib/lib/Lingua/EN/Number/IsOrdinal.pm
Criterion Covered Total %
statement 27 27 100.0
branch 10 10 100.0
condition 8 9 88.8
subroutine 8 8 100.0
pod 1 1 100.0
total 54 55 98.1


line stmt bran cond sub pod time code
1             package Lingua::EN::Number::IsOrdinal;
2             BEGIN {
3 1     1   49400 $Lingua::EN::Number::IsOrdinal::AUTHORITY = 'cpan:AVAR';
4             }
5             {
6             $Lingua::EN::Number::IsOrdinal::VERSION = '0.04';
7             }
8              
9 1     1   11 use strict;
  1         2  
  1         30  
10 1     1   5 use warnings;
  1         2  
  1         32  
11 1     1   5 use Exporter 'import';
  1         2  
  1         32  
12 1     1   917 use Lingua::EN::FindNumber 'extract_numbers';
  1         6661  
  1         505  
13              
14             =encoding UTF-8
15              
16             =head1 NAME
17              
18             Lingua::EN::Number::IsOrdinal - detect if English number is ordinal or cardinal
19              
20             =head1 SYNOPSIS
21              
22             use Lingua::EN::Number::IsOrdinal 'is_ordinal';
23              
24             ok is_ordinal('first');
25              
26             ok !is_ordinal('one');
27              
28             ok is_ordinal('2nd');
29              
30             ok !is_ordinal('2');
31              
32             =head1 DESCRIPTION
33              
34             This module will tell you if a number, either in words or as digits, is a
35             cardinal or L<ordinal
36             number|http://www.ego4u.com/en/cram-up/vocabulary/numbers/ordinal>.
37              
38             This is useful if you e.g. want to distinguish these types of numbers found with
39             L<Lingua::EN::FindNumber> and take different actions.
40              
41             =cut
42              
43             our @EXPORT_OK = qw/is_ordinal/;
44              
45             my $ORDINAL_WORDS_NUMBER_RE = qr/(?:first|second|third|th)\s*$/;
46              
47             my $NUMBER_RE = qr/^\s*(?:[+-]?)(?=\d|\.\d)\d*(?:\.\d*)?(?:[Ee](?:[+-]?\d+))?/;
48              
49             my $CARDINAL_NUMBER_RE = qr/$NUMBER_RE\s*$/;
50              
51             my $ORDINAL_NUMBER_RE = qr/$NUMBER_RE(?:st|nd|rd|th)\s*$/;
52              
53             =head1 FUNCTIONS
54              
55             =head2 is_ordinal
56              
57             Takes a number as English words or digits (with or without ordinal suffix) and
58             returns C<1> for ordinal numbers and C<undef> for cardinal numbers.
59              
60             Checks that the whole parameter is a number using L<Lingua::EN::FindNumber> or
61             a regex in the case of digits, and if it isn't will throw a C<not a number>
62             exception.
63              
64             This function can be optionally imported.
65              
66             =cut
67              
68 19     19 1 8649 sub is_ordinal { __PACKAGE__->_is_ordinal(@_) }
69              
70             =head1 METHODS
71              
72             =head2 _is_ordinal
73              
74             Method version of L</is_ordinal>, this is where the function is actually
75             implemented. Can be overloaded in a subclass.
76              
77             =cut
78              
79             sub _is_ordinal {
80 19     19   27 my ($self, $num) = @_;
81              
82 19 100       44 die "not a number" unless $self->_is_number($num);
83              
84 18 100       174 if ($num =~ $ORDINAL_NUMBER_RE) {
    100          
    100          
85 3         10 return 1;
86             }
87             elsif ($num =~ $CARDINAL_NUMBER_RE) {
88 7         19 return undef;
89             }
90             elsif ($num =~ $ORDINAL_WORDS_NUMBER_RE) {
91 4         14 return 1;
92             }
93              
94 4         24 return undef; # cardinal words-number
95             }
96              
97             =head2 _is_number
98              
99             Returns C<1> if the passed in string is a word-number as detected by
100             L<Lingua::EN::FindNumber> or is a cardinal or ordinal number made of digits and
101             (for ordinal numbers) a suffix. Otherwise returns C<undef>. Can be overloaded in
102             a subclass.
103              
104             =cut
105              
106             sub _is_number {
107 19     19   21 my ($self, $text) = @_;
108 19         105 s/^\s+//, s/\s+$// for $text;
109            
110 19         47 my @nums = extract_numbers $text;
111              
112 19 100 66     492 if ((@nums == 1 && $nums[0] eq $text)
      100        
      100        
113             || $text =~ $ORDINAL_NUMBER_RE || $text =~ $CARDINAL_NUMBER_RE) {
114              
115 18         53 return 1;
116             }
117              
118 1         16 return undef;
119             }
120              
121             =head1 SEE ALSO
122              
123             =over 4
124              
125             =item * L<Lingua::EN::FindNumber>
126              
127             =item * L<Lingua::EN::Words2Nums>
128              
129             =item * L<Lingua::EN::Inflect::Phrase>
130              
131             =back
132              
133             =head1 AUTHOR
134              
135             Rafael Kitover <rkitover@cpan.org>
136              
137             =cut
138              
139             1;