September 09, 2008
Sean Kelly wrote:
> Andrei Alexandrescu wrote:
>>
>> Then there remains the problem:
>>
>> void bump(R, V)(R r)
>> {
>>     for (; !r.isEmpty; r.next) ++r.first;
>> }
>>
>> bump(charArraySucker); // bogus
>>
>> Sigh...
> 
> And I suppose you don't want to return a const reference from first() because the user may want to operate on the value, if only on a temporary basis?  Let's say:
> 
> P findFirst(P, R, C)( R r, C c )
> {
>     for( ; !r.isEmpty; r.next )
>     {
>         // modify the temporary because a new element is expensive
>         // to copy-construct
>         if( auto p = c.contains( ++r.first ) )
>             return p;
>     }
>     return P.init;
> }
> 
> Hm... must the implementation prevent stupid mistakes such as your example?  Ideally, yes.  But I don't see a way to do so and yet allow for routines like findFirst() above.

Yes, exactly. Also consider a user that inspects lines in a file, and occasionally takes ownership of the current line to put it into a hashtable or something.

I think I'll resign myself to isEmpty/next/first for input ranges. The remaining difference between input ranges and forward ranges is that the former are uncopyable.


Andrei
September 09, 2008
Sean Kelly wrote:
> This would be easy to fix by making arrays / slices fatter (by adding a capacity field, for example), but I'm still not convinced that's the right thing to do.  However, it may well be preferable to eliminating appending completely.  The obvious alternative would be either to resurrect head const (not gonna happen) or to make append always reallocation (not at all ideal).

I couldn't imagine it put any better. Maybe time has come for starting to look into a good solution for this problem. The way things are now, ~= muddles the clean territory that slices cover.

Consider we define "unowned" arrays are arrays as allocated by new T[n]. They are "unowned" because no entity controls them except the garbage collector, which by definition recycles them when it's sure you couldn't tell.

An "owned" array would be something like a scope variable or an up-and-coming BlockArray!(T) with a destructor.

Slices are beautiful for iterating owned and unowned arrays just as well. You can have the slice refer to any range of any array no problem. Calling a ~ b creates a new, unowned array containing their concatenation. Assigning a = new T[n]; binds a to a fresh unowned array. And so on.

And all of a sudden we step on a kaka in this beautiful garden. Under very special, undetectable circumstances, a range becomes Hitler, annexes an adjacent range, and starts walking all over it. Sigh.


Andrei
September 09, 2008
Andrei Alexandrescu wrote:
> Walter Bright wrote:
>> BCS wrote:
>>> I was referring to the implementation as visible from the called code's side
>>
>> opApply isn't going to go away, it will still be there as an alternative.
> 
> I disagree with that as I think that would be one perfect place to simplify the language thus fulfilling bearophile's and many others' wish.
> 
> Say we refine ranges to work with foreach to perfection. Then we have:
> 
> 1. A foreach that sucks
> 
> 2. A foreach that rocks
> 
> The obvious question is, why keep the one that sucks?

I agree but i am worried that wont happen. D gets more and more polluted by deprecated and/or ambiguous stuff:

- inout/ref

- opCall/struct-ctor

are some examples. I whished D would only provide unambiguous features. Especially since D2.0 is the experimental branch anyway, so why not clean up finally ?
September 09, 2008
Leandro Lucarella wrote:
> Andrei Alexandrescu, el  9 de septiembre a las 10:30 me escribiste:

>> I'd like to go with:
>>
>> r.first
>> r.last
> 
> Much better, thank you! =)
> 

Maybe:

r.head
r.tail

Also, I'm not crazy about the "isEmpty" name. Given the use of "getNext", I think "hasNext" is a more natural choice.

--benji
September 09, 2008
On Tue, 09 Sep 2008 23:46:27 +0400, Extrawurst <spam@extrawurst.org> wrote:

> Andrei Alexandrescu wrote:
>> Walter Bright wrote:
>>> BCS wrote:
>>>> I was referring to the implementation as visible from the called code's side
>>>
>>> opApply isn't going to go away, it will still be there as an alternative.
>>  I disagree with that as I think that would be one perfect place to simplify the language thus fulfilling bearophile's and many others' wish.
>>  Say we refine ranges to work with foreach to perfection. Then we have:
>>  1. A foreach that sucks
>>  2. A foreach that rocks
>>  The obvious question is, why keep the one that sucks?
>
> I agree but i am worried that wont happen. D gets more and more polluted by deprecated and/or ambiguous stuff:
>
> - inout/ref
>
> - opCall/struct-ctor
>
> are some examples. I whished D would only provide unambiguous features. Especially since D2.0 is the experimental branch anyway, so why not clean up finally ?

I would also add:

invariant float pi1 = 3.1415926;
const float pi2 = 3.1415926;
enum pi3 = 3.1415926;
...
September 09, 2008
"Andrei Alexandrescu" wrote
<snip>

Excellent ideas!  I think the best part is about how you almost never need individual iterators, only ever ranges.  Perfectly explained!

One issue that you might not have considered is using a container as a data structure, and not using it for algorithms.  For example, how would one specify that one wants to remove a specific element, not a range of elements.  Having a construct that points to a specific element but not any specific end element might be a requirement for non-algorithmic reasons.

Also, some ranges might become invalid later on whereas the iterators would not.  Take for example a Hash container.  If you have to rehash the table, a range now might not make any sense, as the 'end' element may have moved to be before the 'begin' element.  But an iterator that points to a given element will still be valid (and could be used for removal).  In fact, I don't think ranges make a lot of sense for things like Hash where there isn't any defined order.  But you still should have a 'pointer' type to support O(1) removal.

One doesn't see any of these problems with arrays, because with arrays, you are guaranteed to have contiguous memory.

What I'm trying to say is there may be a reason to have pointers for certain containers, even though they might be unsafe.

My personal pet peeve of many container implementations is not being able to remove elements from a container while iterating.  For example, I have a linked list of open file descriptors, iterate over the list, closing and removing those that are done (which should be O(1) per removal).  In many container implementations, iteration implies immutable, which means you have to add references to the elements to remove to another list to then remove afterwards (somtimes at the cost of O(n) per removal.  grrrr.)  I hope ranges will support removal while traversing.

That's all I have for now, I have to go think about how this will impact dcollections :)

-Steve


September 09, 2008
Benji Smith wrote:

> Given the use of "getNext", I think "hasNext" is a more natural choice

clapping hands.

-manfred

-- 
If life is going to exist in this Universe, then the one thing it cannot afford to have is a sense of proportion. (Douglas Adams)

September 09, 2008
Brad Roberts wrote:
> Probably rhetorical, but I can't help myself:  If it walks like a duck
> and it talks like a duck, it must be a duck.

And if it floats and has a long nose, it's a witch!! Burn her!!!
September 09, 2008
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:ga6pq4$hqt$1@digitalmars.com...
> Brad Roberts wrote:
>> Probably rhetorical, but I can't help myself:  If it walks like a duck and it talks like a duck, it must be a duck.
>
> And if it floats and has a long nose, it's a witch!! Burn her!!!

And she's got a wart! :)

-steve


September 09, 2008
Manfred_Nowak wrote:
> Benji Smith wrote:
> 
>> Given the use of "getNext", I think "hasNext" is a more natural
>> choice 
> 
> clapping hands.

Walter would love that.

for (R r = getR(); r.hasNext; r.next) { ... }

Look ma, no negation! Oops, I just materialized one with the exclamation sign.


Andrei