15,792,771 members
Articles / Programming Languages / Objective C
Tip/Trick

Value Ranges or Diapasons

Rate me:
4.23/5 (4 votes)
26 Dec 2020CPOL1 min read 5.3K   41   4   2
Proposal for implementation diapasons of values or it ranges
In everyday life, we encounter various ranges or diapasons or ranges - time ranges, money ranges, etc. I propose to write an implementation for them.

Introduction

Every time in every new job, I encounter the same problem. There is no tool to work with diapasons or ranges. I don't know why, but I forced to develop it again and again. For the sake of the future, I will write it once and will reuse.

Solution

The simplest solution is:

C++
```//
// Simple diapason
//
struct ValueRange
{
double start = 0.0;
double stop = 0.0;

// returns distance |start, stop|
double length() const { return stop - start; }
// returns whether u inside this
bool includes(double u) const { return start <= u && u <= stop; }
// returns corrected value (inserted into range, if it was outside)
double included(double v) const;
};```

But somebody may want works with `float`s rather than `double`s. So the code will change to:

C++
```//
// Simple template diapason
//
template<typename T>
struct ValueRangeT
{
using Type = T;

// to be like std::
using value_type = T; // used type

T start{};
T stop{};

// returns distance |start, stop|
T length() const { return stop - start; }
// returns whether u inside this
bool includes(T u) const { return start <= u && u <= stop; }
// returns corrected value (inserted into range, if it was outside)
T included(T v) const;
};```

You will be warned that this `ValueRange` will work only with closed intervals `[start, stop]`. To solve this, let's delegate comparison to the strategy defined later:

C++
```//
// ValueRangeImpl - general diapason from start to stop
// It maybe:
// -closed [start, stop],
// -open (start, stop),
// -half closed/opSen {(start, stop] or [start, stop)}
// -exclude range from all possible values ]start, stop[ =>
//    [-inf, start][stop, inf] or [-inf, start)(stop, inf]
//
// specialization requires comparison strategies (traits)
template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
struct ValueRangeImpl
{
using LTrait = LeftComparisionTrait;
using RTrait = RightComparisionTrait;

// to be like std::
using value_type = T; // used type

T start = {};
T stop = {};

// returns distance |start, stop|
T length() const { return stop - start; }
// returns whether u inside this
bool includes(T u) const;
// returns corrected value (inserted into range, if it was outside)
T included(T v) const;
};```

The i`ncludes()` and `included()` are:

C++
```template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
bool ValueRangeImpl<T, LeftComparisionTrait, RightComparisionTrait>::includes(T u) const
{
return LTrait()(start, u) && RTrait()(stop, u);
}

template<typename T, typename LeftComparisionTrait, typename RightComparisionTrait>
T ValueRangeImpl<T, LeftComparisionTrait, RightComparisionTrait>::included(const T v) const
{
return !LTrait()(start, v) ? start : !RTrait()(stop, v) ? stop : v;
}```

As the comparison strategy may be used `std::less_equal`:

C++
```// final value ranges implementation
// interval, with both boundary included: [a,b]
using ClosedValueRange = ValueRangeImpl<
double,
typename std::less_equal<double>,
typename std::greater_equal<double>>;

// interval, with both boundary excluded: (a,b)
using OpenValueRange = ValueRangeImpl<
double,
typename std::less<double>,
typename std::greater<double>>;

// interval, with excluded left boundary and included right one: (a,b]
using OpenClosedValueRange = ValueRangeImpl<
double,
typename std::less<double>,
typename std::greater_equal<double>>;

// interval, with included left boundary and excluded right one: [a,b)
using ClosedOpenValueRange = ValueRangeImpl<
double,
typename std::less_equal<double>,
typename std::greater<double>>;```

The simplest samples:

C++
```EXPECT_TRUE  (ClosedValueRange(1, 5).includes(1));
EXPECT_TRUE  (ClosedValueRange(1, 5).includes(5));
EXPECT_FALSE (OpenValueRange(1, 5).includes(1));
EXPECT_FALSE (OpenValueRange(1, 5).includes(5));
EXPECT_FALSE (OpenClosedValueRange(1, 5).includes(1));
EXPECT_TRUE  (OpenClosedValueRange(1, 5).includes(5));
EXPECT_TRUE  (ClosedOpenValueRange(1, 5).includes(1));
EXPECT_FALSE (ClosedOpenValueRange(1, 5).includes(5));```

Usage

Let's imagine that we are working under `SuitSellerSolution`. To put the suit on the market, we have to describe its size.

C++
```// first approach to SuitSize
class SuitSize
{
public:
std::string m_name;
ClosedValueRange m_waist;
};

// three sizes for simplicity:
SuitSize sizeS{"S", {78, 82}};
SuitSize sizeL{"M", {82, 86}};
SuitSize sizeM{"S", {86, 90}};```

But what about values `82` and `86`? What size do they belong? Let's use partially closed intervals.

C++
```// SuitSize, which used appropriate interval
template<typename WaistWrap>
class SuitSizeImpl
{
public:
std::string m_name;
WaistWrap m_waist;
};

using SuitSizeS = SuitSizeImpl<ClosedOpenValueRange>;
using SuitSizeM = SuitSizeImpl<ClosedOpenValueRange>;
using SuitSizeL = SuitSizeImpl<ClosedValueRange>;

SuitSizeS sizeS{"S", {78, 82}};
SuitSizeS sizeM{"M", {82, 86}};
SuitSizeM sizeL{"L", {86, 90}};```

Great! There are no conflicts in this `SuitSize` edition.

The next application calculates vacation salary. There is a requirement that vacation includes both boundaries, or somebody has a vacation from 11.07.2027 till 25.07.2027 exclusively, i.e., 26.07.2027 employee should be at work at 8:00 AM.

This implies the presence of class:

C++
```// this class describes vacation
class Vacation
{
using Date = boost::gregorian::date; // may be your own class for Date representation
// Date comparison functors:
// start vacation comparison
struct LeftDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 <= d2;
}
};
// end vacation comparison
struct RightDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 > d2; // open interval
}
};

// Date interval [vacationStart, vacationStop)
using DateValueRange = ValueRangeImpl<Date, LeftDateCompare, RightDateCompare>;

DateValueRange m_duration;
};```

While then the system was demonstrated to users, they found, that usually vacation duration calculates inclusively. The only thing to fix is:

C++
```class Vacation
{
...
// end vacation comparison
struct RightDateCompare
{
bool operator()(const Date& d1, const Date& d2)
{
return d1 >= d2; // open interval
}
};
....
};```

I use `ValueRange` in the definition of the domain of a parametric function, and I will talk about it next time.

History

• 6th December, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Written By
Software Developer (Senior) Ormco
Russian Federation
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 First Prev Next
 Pro: Tackles an often encountered problem ??? Con: Article does not cover the source + small bug in article Member 109506218-Dec-20 3:15 Member 10950621 8-Dec-20 3:15
 Re: Pro: Tackles an often encountered problem ??? Con: Article does not cover the source + small bug in article IgorRadionyuk26-Dec-20 8:56 IgorRadionyuk 26-Dec-20 8:56
 Last Visit: 31-Dec-99 19:00     Last Update: 2-Dec-23 4:50 Refresh 1

General    News    Suggestion    Question    Bug    Answer    Joke    Praise    Rant    Admin

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.