Denis Koroskin wrote:
> On Fri, 27 Feb 2009 16:44:31 +0300, Andrei Alexandrescu
> <[email protected]> wrote:
>> I'm having trouble understanding what's wrong with the good old data
>> types and functions.
>>
>> Andrei
> 
> The syntax. One may want to reuse 0..100 syntax to generate random number:
> auto x = random(0..100); // gimme a random value in [0, 100)
> 
> or check if a value belongs to an interval:
> 
> T opIndex(size_t index)
> {
>    assert(index in 0.._size);
>    // ...
> }

I don't think this should be in the language... it just feels like there
isn't enough use for it.  Especially not when you can implement it as a
library.

As proof, I've attached an implementation of an integral interval.
Here's a sample of usage (the program prints this if you run it):

> inter[0, 10] = inter[0, 10]
> inter[0..10] = inter[0, 9]
> inter(0, 10) = inter[1, 9]

[a,b] is inclusive, (a,b) is exclusive and [a..b] is lower inclusive,
upper exclusive (to match slicing syntax).

> inter[0..10] % 2 = inter[0, 8] / 2
> inter[1..10] % 2 = inter[2, 8] / 2
> inter[1..10] / 2 = inter[1, 9] / 2
> 6 in inter[0..10] % 2 = true
> 7 in inter[0..10] % 2 = false

I % a produces an interval of all values n in I which satisfy:
 n % a == 0.

I / a produces an interval containing every a'th element of I (including
the first).

> inter[0, 30] / 2 = inter[0, 30] / 2
> (inter[0, 30] / 2).length = 16
> (inter[0, 30] / 2) / 3 = inter[0, 30] / 6
> ((inter[0, 30] / 2) / 3).length = 6
> ((inter[0, 30] / 2) / 3).toArray = [0 6 12 18 24 30]

It should support indexing, slicing and length-ing.  It should also
support ranges (can't test it, of course :P).


  -- Daniel


module interval;

import std.string;
import std.traits : isIntegral;

struct Interval(T)
{
    static assert( isIntegral!(T),
            "only intervals over the integers are supported.");

    T min = T.init,
      max = T.init-1,
      stride = 1;

    this(T min, T max)
    {
        if( max < min )
            max = min;

        this.min = min;
        this.max = max;
        this.stride = stride;
    }

    this(T min, T max, T stride)
    {
        // Ensure max is actually in the interval
        max = min + stride*((max-min)/stride);

        if( max < min )
            max = min;

        this.min = min;
        this.max = max;
        this.stride = stride;
    }

    string toString() const
    {
        if( stride == 1 )
            return format("inter[%s, %s]", min, max);

        else
            return format("inter[%s, %s] / %s", min, max, stride);
    }

    pure bool opIn_r(T v) const
    {
        if( stride == 1 )
            return (min <= v && v <= max);

        else
        {
            return (min <= v && v <= max)
                && ((v-min) % stride == 0);
        }
    }

    Interval opDiv(T stride) const
    {
        if( stride == 0 )
            throw new Exception("intervals cannot have a zero stride");

        return Interval(min, max, this.stride*stride);
    }

    Interval opMod(T divisor) const
    {
        auto new_stride = stride*divisor;

        return Interval(
                min + min%(new_stride),
                max,
                new_stride);
    }

    /+
    Interval opAnd(Interval other) const
    {
        auto new_min = (this.min > other.min) ? this.min : other.min;
        auto new_max = (this.max < other.max) ? this.max : other.max;

        if( this.stride == 1 && other.stride == 1 )
            return Interval(new_min, new_max);

        /*
           The problem now is determining the new stride and the new
           minimum.  Consider the following:

           (inter[0..10] / 2) & (inter[0..10] / 3)
            == [0, 2, 4, 6, 8] & [0, 3, 6, 9]
            == [0, 6]

           (inter[0..10] / 2) & (inter[1..10] / 2)
            == []

           (inter[0..10] / 2) & (inter[1..10] / 3)
            == [0, 2, 4, 6, 8] & [1, 4, 7]
            == [4]
        */
    }
    +/

    pure size_t length() const
    {
        if( stride == 1 )
            return (max-min) + 1;
        else
            return (max-min)/stride + 1;
    }

    pure T[] toArray() const
    {
        auto arr = new T[length];
        auto p = &arr[0];
        for( T i = min; i <= max; i += stride )
            *(p++) = i;
        return arr;
    }

    int opApply(int delegate(ref T) dg) const
    {
        int r = 0;
        for( T i = min; i <= max; i += stride )
        {
            auto v = i;
            r = dg(v);
            if( r )
                break;
        }
        return r;
    }

    /*
     *
     * Range interface
     *
     */

    pure bool empty() const
    {
        return (max<min);
    }

    pure T head() const
    {
        return min;
    }

    pure T toe() const
    {
        return max;
    }

    void next()
    {
        if( min > T.max-stride )
            max -= stride;
        else
            min += stride;
    }

    void retreat()
    {
        if( max < T.min+stride )
            min += stride;
        else
            max -= stride;
    }

    pure T opIndex(size_t offset) const
    {
        if( offset >= length )
            throw new Exception("out of bounds of interval");
        
        return min + stride*offset;
    }

    Interval opSlice(size_t a, size_t b) const
    {
        auto l = length;
        if( a >= length || b >= length )
            throw new Exception("out of bounds of interval");

        return Interval(
                min + stride*a,
                max + stride*b,
                stride);
    }
}

struct inter
{
    static Interval!(T) opCall(T)(T a, T b)
    {
        return Interval!(T)(a+1, b-1);
    }

    static Interval!(T) opIndex(T)(T a, T b)
    {
        return Interval!(T)(a, b);
    }

    static Interval!(T) opSlice(T)(T a, T b)
    {
        return Interval!(T)(a, b-1);
    }
}

import std.stdio : writef, writefln;

string trim(string s)
{
    while( s.length > 0 && (s[0] == '\t' || s[0] == ' ') )
        s = s[1..$];

    while( s.length > 0 && (s[$-1] == '\t' || s[$-1] == ' ') )
        s = s[0..$-1];

    return s;
}

string dump(string expr)
{
    return
    `
        writefln("%s = %s"`~`, "`~trim(expr)~`", `~trim(expr)~`);
    `;
}

void main()
{
    mixin(dump(q{   inter[0, 10]                                        }));
    mixin(dump(q{   inter[0..10]                                        }));
    mixin(dump(q{   inter(0, 10)                                        }));
    writefln("");
    mixin(dump(q{   inter[0..10] % 2                                    }));
    mixin(dump(q{   inter[1..10] % 2                                    }));
    mixin(dump(q{   inter[1..10] / 2                                    }));
    mixin(dump(q{   6 in inter[0..10] % 2                               }));
    mixin(dump(q{   7 in inter[0..10] % 2                               }));
    writefln("");
    mixin(dump(q{   inter[0, 30] / 2                                    }));
    mixin(dump(q{  (inter[0, 30] / 2).length                            }));
    mixin(dump(q{  (inter[0, 30] / 2) / 3                               }));
    mixin(dump(q{ ((inter[0, 30] / 2) / 3).length                       }));
    mixin(dump(q{ ((inter[0, 30] / 2) / 3).toArray                      }));
}

/*
Output:

inter[0, 10] = inter[0, 10]
inter[0..10] = inter[0, 9]
inter(0, 10) = inter[1, 9]

inter[0..10] % 2 = inter[0, 8] / 2
inter[1..10] % 2 = inter[2, 8] / 2
inter[1..10] / 2 = inter[1, 9] / 2
6 in inter[0..10] % 2 = true
7 in inter[0..10] % 2 = false

inter[0, 30] / 2 = inter[0, 30] / 2
(inter[0, 30] / 2).length = 16
(inter[0, 30] / 2) / 3 = inter[0, 30] / 6
((inter[0, 30] / 2) / 3).length = 6
((inter[0, 30] / 2) / 3).toArray = [0 6 12 18 24 30]
*/

Reply via email to