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]
*/