September 09, 2004
In article <chpqhu$2a2p$1@digitaldaemon.com>, Ant says...
>
>In article <chppt1$29fr$1@digitaldaemon.com>, Stewart Gordon says...
>>
>>Matthew wrote:
>>
>>> DTL provides the IntegralRange template that does exactly what you need.
>>
>>Need?
>>
>>>     foreach(int i; new IntegralRange(int)(-10, 10, 2)
>><snip>
>>
>>     for (int i = -10; i < 10; i += 2)
>>
>>is not only shorter to type, it also has much less overhead.
>
>that's the first impression.
>but there must be a usefulness the IntegralRange. What can it be?
>anyone care to enlighten us?
>

I'll take a stab at this one. :)

How about pluggability, assuming that IntegralRange inherits from a more generic base?  One could pass a range to a method, that could be virtually anything: an IntegralRange, an iterator wrapper of some kind, maybe even some kind of stream wrapper.

# void doSomething(Range!(int) r){
#    foreach(int i,value; r){
#        writefln("range[%d] = %d",i,value);
#    }
# }
#
# void main(){
#     int[] list;
#     doSomething(new IntegralRange!(int)(-10,10,2)); // see above
#     doSomething(new FullRange!(int)(list)); // iterator wrapper
#     doSomething(new RandomRange!(int)(list)); // random, non-repeating
sequence
# }

I'm no STL programmer, but I've always understood the advantage in generics to be that you can plug-in and substitute things all over the place.  This is just another example.

- Pragma
[[ Eric Anderton (Sig!()) at yahoo dot com ]]
September 09, 2004
In article <choprh$1njb$1@digitaldaemon.com>, Matthew says...
>
>DTL provides the IntegralRange template that does exactly what you need.
>
>    foreach(int i; new IntegralRange(int)(-10, 10, 2)
>    {
>        printf("%d ", i);
>    }
>
>This prints
>
>    "-10 -8 -6 -4 -2 0 2 4 6 8"
>
>The slight uglification is the need to "new", but that's out of my hands. ;)

Why not offer a static opApply to generate the range?

# class IntegralRange(T)
# {
# public:
#     static IntegralRange opCall( T beg, T end, T step )
#     {
#         return new IntegralRange( beg, end, step );
#     }
#
#     this( T beg, T end, T step ) {}
# }
# ...
# foreach(int i; IntegralRange!(int)(-10, 10, 2) )
# {
#     printf("%d ", i);
# }

Frankly, I'm half-tempted to always define static opCall constructor wrappers for classes just to simplify construction.  In that vein, it might be nice for this to be legal:

int x = int( 5 );

This would be a reasonable compliment to the pointer construction method:

int* x = new int( 5 );

and it would allow for complete generic construction of standard types, albeit with a bit of work on the class side.


Sean


September 09, 2004
"Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:choprh$1njb$1@digitaldaemon.com...
> DTL provides the IntegralRange template that does exactly what you need.
>
>     foreach(int i; new IntegralRange(int)(-10, 10, 2)
>     {
>         printf("%d ", i);
>     }
>
> This prints
>
>     "-10 -8 -6 -4 -2 0 2 4 6 8"
>
> The slight uglification is the need to "new", but that's out of my hands.
;)
>
> Check the D.dtl NG for details of where to get the latest distro.


struct range(T, T step = 1)
{
   private struct Foreach
   {
      T start, stop;
      int opApply(int delegate(inout T) dg)
      {
         int result;
         T val;
         for(val = start; val < stop; val += step)
         {
            result = dg(val);
            if(result)
               break;
         }
         return result;
      }
   }

   static Foreach opSlice(T start, T stop)
   in
   {
      assert(start <= stop);
   }
   body
   {
      Foreach fe;
      fe.start = start;
      fe.stop = stop;
      return fe;
   }
}

int main()
{
   range!(int) f;
   foreach(int foo; f[30 .. 100])
   {
      printf("%d\t", foo);
   }
   return 0;
}


I didn't need "new"...
But for some reason range!(int)[30 .. 100] won't work: "struct range cannot
be sliced with []".


September 09, 2004
Funny, I've started avoiding static opCalls. They are basically a hack. For structs it is very easy for user code to forget to make the static opCall, and so pretty much every member function still needs to worry about structs with the default initialization. Also for structs it means you have to type out the struct name twice, which is annoying. For classes saving 4 keystrokes "new " doesn't seem worth it IMO.

"Sean Kelly" <sean@f4.ca> wrote in message news:chq3uo$2f9s$1@digitaldaemon.com...
> In article <choprh$1njb$1@digitaldaemon.com>, Matthew says...
> >
> >DTL provides the IntegralRange template that does exactly what you need.
> >
> >    foreach(int i; new IntegralRange(int)(-10, 10, 2)
> >    {
> >        printf("%d ", i);
> >    }
> >
> >This prints
> >
> >    "-10 -8 -6 -4 -2 0 2 4 6 8"
> >
> >The slight uglification is the need to "new", but that's out of my hands.
;)
>
> Why not offer a static opApply to generate the range?
>
> # class IntegralRange(T)
> # {
> # public:
> #     static IntegralRange opCall( T beg, T end, T step )
> #     {
> #         return new IntegralRange( beg, end, step );
> #     }
> #
> #     this( T beg, T end, T step ) {}
> # }
> # ...
> # foreach(int i; IntegralRange!(int)(-10, 10, 2) )
> # {
> #     printf("%d ", i);
> # }
>
> Frankly, I'm half-tempted to always define static opCall constructor
wrappers
> for classes just to simplify construction.  In that vein, it might be nice
for
> this to be legal:
>
> int x = int( 5 );
>
> This would be a reasonable compliment to the pointer construction method:
>
> int* x = new int( 5 );
>
> and it would allow for complete generic construction of standard types,
albeit
> with a bit of work on the class side.
>
>
> Sean
>
>


September 09, 2004
In article <chq4ss$2fke$1@digitaldaemon.com>, Ben Hinkle says...
>
>Funny, I've started avoiding static opCalls. They are basically a hack. For structs it is very easy for user code to forget to make the static opCall, and so pretty much every member function still needs to worry about structs with the default initialization. Also for structs it means you have to type out the struct name twice, which is annoying. For classes saving 4 keystrokes "new " doesn't seem worth it IMO.
>

Hmm, nah, they make sense for things like:

class Base!(RADIX) { // some base converter
int opCall(char[]) { }
char[] opCall(int) { }
}
Base!(10) dec = new Base!(10);
dec(10); // "10"
dec("10"); // 10

:)


September 09, 2004
"Vathix" <vathixSpamFix@dprogramming.com> wrote in message news:chq45i$2fbu$1@digitaldaemon.com...
> "Matthew" <admin@stlsoft.dot.dot.dot.dot.org> wrote in message news:choprh$1njb$1@digitaldaemon.com...
> > DTL provides the IntegralRange template that does exactly what you need.
> >
> >     foreach(int i; new IntegralRange(int)(-10, 10, 2)
> >     {
> >         printf("%d ", i);
> >     }
> >
> > This prints
> >
> >     "-10 -8 -6 -4 -2 0 2 4 6 8"
> >
> > The slight uglification is the need to "new", but that's out of my
hands.
> ;)
> >
> > Check the D.dtl NG for details of where to get the latest distro.
>
>
> struct range(T, T step = 1)
> {
>    private struct Foreach
>    {
>       T start, stop;
>       int opApply(int delegate(inout T) dg)
>       {
>          int result;
>          T val;
>          for(val = start; val < stop; val += step)
>          {
>             result = dg(val);
>             if(result)
>                break;
>          }
>          return result;
>       }
>    }
>
>    static Foreach opSlice(T start, T stop)
>    in
>    {
>       assert(start <= stop);
>    }
>    body
>    {
>       Foreach fe;
>       fe.start = start;
>       fe.stop = stop;
>       return fe;
>    }
> }
>
> int main()
> {
>    range!(int) f;
>    foreach(int foo; f[30 .. 100])
>    {
>       printf("%d\t", foo);
>    }
>    return 0;
> }
>
>
> I didn't need "new"...
> But for some reason range!(int)[30 .. 100] won't work: "struct range
cannot
> be sliced with []".
>

based on your code here's one using aliases to define something easier on the eyes:

struct Range(T) {
 T begin, end, step;
 static Range make(T begin, T end, T step = 1) {
   Range res;
   res.begin = begin;
   res.end = end;
   res.step = step;
   return res;
 }
 int opApply(int delegate(inout T x) dg) {
   int res;
   for(T x = begin; x < end; x += step) {
     res = dg(x);
     if (res) break;
   }
   return res;
 }
}

// some pre-defined aliases for common range types
alias Range!(byte).make   range;
alias Range!(short).make  range;
alias Range!(int).make    range;
alias Range!(uint).make   range;
alias Range!(long).make   range;
alias Range!(double).make range;

int main() {
  foreach(int x; range(0,10,2)) {
    printf("%d\n",x);
  }
  foreach(double x; range(1.0,3.5,.5)) {
    printf("%g\n",x);
  }
  foreach(float x; Range!(float).make(1.0,3.5,.5)) {
    printf("%g\n",x);
  }
  return 0;
}



September 09, 2004
In article <chq4ss$2fke$1@digitaldaemon.com>, Ben Hinkle says...
>
>Funny, I've started avoiding static opCalls. They are basically a hack. For structs it is very easy for user code to forget to make the static opCall, and so pretty much every member function still needs to worry about structs with the default initialization. Also for structs it means you have to type out the struct name twice, which is annoying. For classes saving 4 keystrokes "new " doesn't seem worth it IMO.

True enough.  I was mostly looking for a way to handle generic type construction without specialized wrapper functions.  Of course any method that relies on user cooperation is not likely to succeed, so perhaps the static opCall trick isn't particularly useful.  Still, it's better than a free function which does the same thing (which would be the C++ method).


Sean


September 09, 2004
But what about when you want to traverse a range from two arbitrary integers, with arbitrary step? i.e. when you don't know, a priori, whether you need a ++, --, +=, or -=?

There are other advantages, e.g. generics, to anyone caring to delve into the subject

"Stewart Gordon" <smjg_1998@yahoo.com> wrote in message news:chppt1$29fr$1@digitaldaemon.com...
> Matthew wrote:
>
>> DTL provides the IntegralRange template that does exactly what you need.
>
> Need?
>
>>     foreach(int i; new IntegralRange(int)(-10, 10, 2)
> <snip>
>
>     for (int i = -10; i < 10; i += 2)
>
> is not only shorter to type, it also has much less overhead.
>
> Stewart.
>
> -- 
> My e-mail is valid but not my primary mailbox.  Please keep replies on the 'group where everyone may benefit.



September 09, 2004
"Sean Kelly" <sean@f4.ca> wrote in message news:chq3uo$2f9s$1@digitaldaemon.com...
> In article <choprh$1njb$1@digitaldaemon.com>, Matthew says...
>>
>>DTL provides the IntegralRange template that does exactly what you need.
>>
>>    foreach(int i; new IntegralRange(int)(-10, 10, 2)
>>    {
>>        printf("%d ", i);
>>    }
>>
>>This prints
>>
>>    "-10 -8 -6 -4 -2 0 2 4 6 8"
>>
>>The slight uglification is the need to "new", but that's out of my hands. ;)
>
> Why not offer a static opApply to generate the range?
>
> # class IntegralRange(T)
> # {
> # public:
> #     static IntegralRange opCall( T beg, T end, T step )
> #     {
> #         return new IntegralRange( beg, end, step );
> #     }
> #
> #     this( T beg, T end, T step ) {}
> # }
> # ...
> # foreach(int i; IntegralRange!(int)(-10, 10, 2) )
> # {
> #     printf("%d ", i);
> # }
>
> Frankly, I'm half-tempted to always define static opCall constructor wrappers for classes just to simplify construction.  In that vein, it might be nice for this to be legal:
>
> int x = int( 5 );
>
> This would be a reasonable compliment to the pointer construction method:
>
> int* x = new int( 5 );
>
> and it would allow for complete generic construction of standard types, albeit with a bit of work on the class side.

I've been toying with the idea, but I don't like the hack-nature of them.

Nonetheless, it could be worth giving it a road test. I think we might see your suggestion reified in the next release of DTL. :-)




September 10, 2004
In article <chq5gn$2g0f$1@digitaldaemon.com>, Joey Peters says...
>
>In article <chq4ss$2fke$1@digitaldaemon.com>, Ben Hinkle says...
>>
>>Funny, I've started avoiding static opCalls. They are basically a hack. For structs it is very easy for user code to forget to make the static opCall, and so pretty much every member function still needs to worry about structs with the default initialization. Also for structs it means you have to type out the struct name twice, which is annoying. For classes saving 4 keystrokes "new " doesn't seem worth it IMO.
>>
>
>Hmm, nah, they make sense for things like:
>
>class Base!(RADIX) { // some base converter
>int opCall(char[]) { }
>char[] opCall(int) { }
>}
>Base!(10) dec = new Base!(10);
>dec(10); // "10"
>dec("10"); // 10
>
>:)

Joey, Ben was talking about /static/ opCall() - something of limited use.
Your example is a /non-static/ opCall() - something profoundly useful.

There really should be a FAQ about this distinction, as s-o  m-a-n-y times we see this mistake cropping up.

Arcane Jill