Number::Tolerant -- tolerance ranges for inexact numbers
version 1.42
$Id: /my/cs/projects/tolerant/trunk/lib/Number/Tolerant.pm 18116 2006-01-27T02:41:47.507682Z rjbs $
use Number::Tolerant; my $range = tolerance(10 => to => 12); my $random = 10 + rand(2); die "I shouldn't die" unless $random == $range; print "This line will always print.\n";
Number::Tolerant creates a number-like object whose value refers to a range of possible values, each equally acceptable. It overloads comparison operations to reflect this.
I use this module to simplify the comparison of measurement results to specified tolerances.
reject $product unless $measurement == $specification;
Number::Tolerance->new( ... )
tolerance( ... )
There is a new method on the Number::Tolerant class, but it also exports a simple function, tolerance, which will return an object of the Number::Tolerant class. Both use the same syntax:
new
tolerance
my $range = tolerance( $x => $method => $y);
The meaning of $x and $y are dependant on the value of $method, which describes the nature of the tolerance. Tolerances can be defined in five ways, at present:
$x
$y
$method
method range -------------------+------------------ plus_or_minus | x ± y plus_or_minus_pct | x ± (y% of x) or_more | x to Inf or_less | x to -Inf more_than | x to Inf, not x less_than | x to -Inf, not x to | x to y infinite | -Inf to Inf offset | (x + y1) to (x + y2)
For or_less and or_more, $y is ignored if passed. For infinite, neither $x nor $y is used; "infinite" should be the sole argument. The first two arguments can be reversed for more_than and less_than, to be more English-like.
or_less
or_more
infinite
more_than
less_than
Offset tolerances are slightly unusual. Here is an example:
my $offset_tolerance = tolerance(10 => offset => (-3, 5)); # stringifies to: 10 (-3 +5)
An offset is very much like a plus_or_minus tolerance, but its center value is not necessarily the midpoint between its extremes. This is significant for comparisons and numifications of the tolerance. Given the following two tolerances:
plus_or_minus
my $pm_dice = tolerance(10.5 => plus_or_minus => 7.5); my $os_dice = tolerance(11 => offset => (-8, 7));
The first will sort as numerically less than the second.
from_string($stringification)
A new tolerance can be instantiated from the stringification of an old tolerance. For example:
my $range = Number::Tolerant->from_string("10 to 12"); die "Everything's OK!" if 11 == $range; # program dies of joy
This will not yet parse stringified unions, but that will be implemented in the future. (I just don't need it yet.)
stringify_as($type)
This method does nothing! Someday, it will stringify the given tolerance as a different type, if possible. "10 +/- 1" will stringify_as('plus_or_minus_pct') to "10 +/- 10%" for example.
stringify_as('plus_or_minus_pct')
numify
This returns the numeric form of a tolerance. If a tolerance has both a minimum and a maximum, and they are the same, then that is the numification. Otherwise, numify returns undef.
Tolerances overload a few operations, mostly comparisons.
Tolerances are always true.
Most tolerances numify to undef; see "numify".
"numify"
A tolerance stringifies to a short description of itself, generally something like "m < x < n"
infinite - "any number" to - "m <= x <= n" or_more - "m <= x" or_less - "x <= n" more_than - "m < x" less_than - "x < n" plus_or_minus - "x +/- y" plus_or_minus_pct - "x +/- y%"
A number is equal to a tolerance if it is neither less than nor greater than it. (See below).
A number is greater than a tolerance if it is greater than its maximum value.
A number is less than a tolerance if it is less than its minimum value.
No number is greater than an "or_more" tolerance or less than an "or_less" tolerance.
"...or equal to" comparisons include the min/max values in the permissible range, as common sense suggests.
A tolerance & a tolerance or number is the intersection of the two ranges. Intersections allow you to quickly narrow down a set of tolerances to the most stringent intersection of values.
&
tolerance(5 => to => 6) & tolerance(5.5 => to => 6.5); # this yields: tolerance(5.5 => to => 6)
If the given values have no intersection, () is returned.
()
An intersection with a normal number will yield that number, if it is within the tolerance.
A tolerance | a tolerance or number is the union of the two. Unions allow multiple tolerances, whether they intersect or not, to be treated as one. See Number::Tolerant::Union for more information.
|
This feature is slighly experimental, but it's here. Custom tolerance types can be created by adding entries to the hash returned by the _tolerance_type method. Keys are package names, and values are ignored. (This registration interface is all but sure to be rewritten in the near future.)
_tolerance_type
The packages should contain classes that subclass Number::Tolerant, providing at least these methods:
construct - returns the reference to be blessed into the tolerance object parse - used by from_string; returns the object that represents the string or undef, if the string doesn't represent this kind of tolerance valid_args - passed args from ->new() or tolerance(); if they indicate this type of tolerance, this sub returns args to be passed to construct
The Number::Tolerant constructor looks through the list of packages for one whose valid_args likes the arguments passed to the constructor. That package's construct is used to build the guts of the object. (This is a simplification; some other logic is applied, including passing literal numbers through unblessed by default.)
valid_args
construct
Extend from_string to cover unions.
from_string
Extend from_string to include Number::Range-type specifications.
Allow translation into forms not originally used:
$range = tolerance(9 => to => 17); $range->convert_to('plus_minus'); $range->stringify_as('plus_minus_pct');
stringify_as can be faked, for a few tolerance types, with something like this:
stringify_as
Number::Tolerance->_tolerance_type->{'destination_type'}->stringify($range);
Besides being ugly, it's a side-effect that isn't tested or guaranteed to work very often.
Break the basic types into their own modules and use Module::Pluggable.
The module Number::Range provides another way to deal with ranges of numbers. The major differences are: N::R is set-like, not range-like; N::R does not overload any operators. Number::Tolerant will not (like N::R) attempt to parse a textual range specification like "1..2,5,7..10" unless specifically instructed to. (The valid formats for strings passed to from_string does not match Number::Range exactly. See TODO.)
The Number::Range code:
Number::Range
$range = Number::Range->new("10..15","20..25");
Is equivalent to the Number::Tolerant code:
Number::Tolerant
$range = Number::Tolerant::Union->new(10..15,20..25);
...while the following code expresses an actual range:
$range = tolerance(10 => to => 15) | tolerance(20 => to => 25);
Thanks to Yuval Kogman and #perl-qa for helping find the bizarre bug that drove the minimum required perl up to 5.8
Ricardo SIGNES, <rjbs@cpan.org>
(C) 2004-2006, Ricardo SIGNES. Number::Tolerant is available under the same terms as Perl itself.
1 POD Error
The following errors were encountered while parsing the POD:
Non-ASCII character seen before =encoding in '±'. Assuming UTF-8
To install Number::Tolerant, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Number::Tolerant
CPAN shell
perl -MCPAN -e shell install Number::Tolerant
For more information on module installation, please visit the detailed CPAN module installation guide.