Click here to Skip to main content
15,881,938 members
Articles / Programming Languages / Objective C
Tip/Trick

Value Ranges or Diapasons

Rate me:
Please Sign up or sign in to vote.
4.23/5 (4 votes)
26 Dec 2020CPOL1 min read 5.5K   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 floats rather than doubles. 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 includes() 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 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

 
QuestionPro: Tackles an often encountered problem ??? Con: Article does not cover the source + small bug in article Pin
Member 109506218-Dec-20 2:15
Member 109506218-Dec-20 2:15 
AnswerRe: Pro: Tackles an often encountered problem ??? Con: Article does not cover the source + small bug in article Pin
IgorRadionyuk26-Dec-20 7:56
IgorRadionyuk26-Dec-20 7:56 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

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