monarch_dodra | 30 Jun 2012 13:31
Picon

Creating a Sub-view of a non - RA (hasSlicing) range.

I've been enjoying my time with D's ranges, but something is 
nagging at me: If the range is not random access, then how does 
one create a sub-view of that range?

Say I have a forward range (for example, an SList[]). I would 
like to create a new range containing only the first 5 elements 
of that old range. How can I do that?

I see no logical reason that prevents me from doing it, since C++ 
iterators can do it quite fine. (the C++ algorithms that work 
only on RA ranges is mostly for efficiency reasons). Not being 
able to do this would severely limit the amount of containers 
that can seamlessly merge with algorithms.

For example, I can't call splitter on an SList. Not sure if this 
is just "not currently supported", or just not possible...

Any thoughts?

Tobias Pankrath | 30 Jun 2012 13:35

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

> Say I have a forward range (for example, an SList[]). I would 
> like to create a new range containing only the first 5 elements 
> of that old range. How can I do that?
>

std.algorithm.take.

But I would generally avoid SList.

monarch_dodra | 30 Jun 2012 14:24
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Saturday, 30 June 2012 at 11:35:07 UTC, Tobias Pankrath wrote:
> std.algorithm.take.
>
> But I would generally avoid SList.

Thanks, but isn't that kind of a crutch solution?

I mean:

1) It creates a new type, so any attempt to use it to modify an 
existing range becomes impossible:
void main()
{
   SList!int slist;
   foreach(i; iota(5,0,-1))
     slist.insertFront(i);

   sr1 = take(sr1, 2); //Nope, not gonna happen
   writeln(sr1);
}

2) The new range is defined as a fixed length from the beginning 
of the range, as opposed to start and finish points. If I were to 
insert new items into my Slist, the new range would just bump the 
top items out of its range.

3) This doesn't work for BidirectionalRanges: The resulting range 
is forward only. For the same reason, it is not possible to have 
a TakeLast for bidirectional ranges.

(Continue reading)

Andrei Alexandrescu | 30 Jun 2012 16:22

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 6/30/12 8:24 AM, monarch_dodra wrote:
> On Saturday, 30 June 2012 at 11:35:07 UTC, Tobias Pankrath wrote:
>> std.algorithm.take.
>>
>> But I would generally avoid SList.
>
> Thanks, but isn't that kind of a crutch solution?
>
> I mean:
>
> 1) It creates a new type, so any attempt to use it to modify an existing
> range becomes impossible:
> void main()
> {
> SList!int slist;
> foreach(i; iota(5,0,-1))
> slist.insertFront(i);
>
> sr1 = take(sr1, 2); //Nope, not gonna happen
> writeln(sr1);
> }

One can't expect that to happen more than suspending a law of physics. 
The state needed for iterating an entire slist is a pointer. The state 
needed for iterating at most n elements of an slist is a pointer plus 
and integer. They're just different notions.

> 2) The new range is defined as a fixed length from the beginning of the
> range, as opposed to start and finish points. If I were to insert new
> items into my Slist, the new range would just bump the top items out of
(Continue reading)

Monarch Dodra | 30 Jun 2012 17:15
Picon
Gravatar

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Saturday, 30 June 2012 at 14:22:06 UTC, Andrei Alexandrescu 
wrote:
>> 2) The new range is defined as a fixed length from the 
>> beginning of the
>> range, as opposed to start and finish points. If I were to 
>> insert new
>> items into my Slist, the new range would just bump the top 
>> items out of
>> its range.
>
> SList's range is not defined by start and finish points. It's 
> defined as the start point, and the finish point is implicit by 
> use of a sentinel (the null pointer).
Well, what about in the case of a BidirRange from a BDList? 
Surelly, it would be defined by a start and finish point?

Take, on the other hand, creates a range, which is defined by a 
start point, and a fixed length. Inserting elements into the 
middle of the original list would bump elements out of the "take 
range", which remains fixed sized.

On the other hand, had I manually shrunk my BDlistRange until it 
was 5 elements long, and then inserted elements into the midle of 
the list, it would cause my BDListRange to grow, and nothing to 
drop out of it.

On Saturday, 30 June 2012 at 14:22:06 UTC, Andrei Alexandrescu 
wrote:
> On 6/30/12 8:24 AM, monarch_dodra wrote:
>> 3) This doesn't work for BidirectionalRanges: The resulting 
(Continue reading)

Andrei Alexandrescu | 1 Jul 2012 01:51

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 6/30/12 11:15 AM, Monarch Dodra wrote:
> On Saturday, 30 June 2012 at 14:22:06 UTC, Andrei Alexandrescu wrote:
>>> 2) The new range is defined as a fixed length from the beginning of the
>>> range, as opposed to start and finish points. If I were to insert new
>>> items into my Slist, the new range would just bump the top items out of
>>> its range.
>>
>> SList's range is not defined by start and finish points. It's defined
>> as the start point, and the finish point is implicit by use of a
>> sentinel (the null pointer).
> Well, what about in the case of a BidirRange from a BDList? Surelly, it
> would be defined by a start and finish point?

Yes. But now you're moving the goalposts because many of your arguments 
referred to SList.

> Take, on the other hand, creates a range, which is defined by a start
> point, and a fixed length. Inserting elements into the middle of the
> original list would bump elements out of the "take range", which remains
> fixed sized.

Correct. take() and takeExactly() work under the assumption there's no 
surreptitious change in the structure of the range underneath. I think 
that's a reasonable decision.

> On the other hand, had I manually shrunk my BDlistRange until it was 5
> elements long, and then inserted elements into the midle of the list, it
> would cause my BDListRange to grow, and nothing to drop out of it.

Right. Generally ranges are not responsible for accurately tracking 
(Continue reading)

monarch_dodra | 1 Jul 2012 15:22
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Saturday, 30 June 2012 at 23:51:42 UTC, Andrei Alexandrescu 
wrote:
> Yes. But now you're moving the goalposts because many of your 
> arguments referred to SList.
I'm sorry, that's not what I meant to do. The goal post was not 
moved, rather misplaced to begin with. Using SList for making my 
point was not the best choice of containers. I'd hae used a 
"DList" like container if I had one.

On Saturday, 30 June 2012 at 23:51:42 UTC, Andrei Alexandrescu 
wrote:
> Correct. take() and takeExactly() work under the assumption 
> there's no surreptitious change in the structure of the range 
> underneath. I think that's a reasonable decision.
Most of the time yes. But for certain "partitioning" algorithms, 
those that _cut_ a range into several subranges (for example 
"findSplit"), such "surreptitious" change should (IMO) be 
expected and supported.

On Saturday, 30 June 2012 at 23:51:42 UTC, Andrei Alexandrescu 
wrote:
> Which is as it should be. How would you otherwise take the 
> first n elements of a given doubly-linked list in constant time?
Constanst time, I don't know. I'm still looking for a way to do 
it in o(N) ;)

On Saturday, 30 June 2012 at 23:51:42 UTC, Andrei Alexandrescu 
wrote:
> It's all about what it takes to reach a certain point. You are 
> sensing something indeed, but take() is not it. The problem is 
(Continue reading)

monarch_dodra | 1 Jul 2012 16:48
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Sunday, 1 July 2012 at 13:22:17 UTC, monarch_dodra wrote:
> Something like...
> "inverseRangeBegin(R big, R small)": Creates a range that 
> begins where big starts, and ends where small starts. R must be 
> at least bidirectional. small must be included inside big.
>
> From an implementation point of view, I think any container, 
> and even arrays, can do it. I pretty sure this is compeltly 
> feasable. Just off the top of my head. I'm sure there are 
> brighter thinkers out there that have put more thought into it.

Or not. On second thought, while I'm sure there might be an 
implementable solution, I doubt it could be done with any real 
safety guarantees.

Christophe Travert | 2 Jul 2012 13:50

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

Have you had a look at dcollection ?

http://www.dsource.org/projects/dcollections

There is a doubly linked list implementation, with range and cursors 
(entities that have iterator functionalities).

bearophile | 30 Jun 2012 14:27
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

Tobias Pankrath:

> But I would generally avoid SList.

It's not good.

And in general linked lists are quite overrated. On modern CPUs 
it's not easy to find situations where a linked list is better 
than a dynamic array or a chunked array (that is a dynamic array 
of pointers to arrays. It's often used to implement deques, etc).

Regarding arrays, maybe a StableVector will be good to have in 
Phobos:
http://www.boost.org/doc/libs/1_50_0_beta1/doc/html/container/non_standard_containers.html#container.non_standard_containers.stable_vector

Bye,
bearophile

monarch_dodra | 30 Jun 2012 15:12
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Saturday, 30 June 2012 at 12:27:38 UTC, bearophile wrote:
> Tobias Pankrath:
>
>> But I would generally avoid SList.
>
> It's not good.
>
> And in general linked lists are quite overrated.
> ...
> Bye,
> bearophile

I appreciate the input, which I (mostly) agree with (I still love 
list's splice ability, which can be very powerful), but this 
isn't really what the question is about.

It's about getting generic algorithms to work with any generic 
range. Right now, they appear (to me) to heavily favor RA, even 
when they should theoretically support simple bidirectionality...

Those that do support directionality do it using "cheat" using 
take. For example:

void main()
{
   SList!int slist;
   foreach(i; iota(5,0,-1))
     slist.insertFront(i);

   auto rnge = slist[];
(Continue reading)

Brad Anderson | 3 Jul 2012 18:00
Gravatar

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On Sat, Jun 30, 2012 at 6:27 AM, bearophile <bearophileHUGS <at> lycos.com> wrote:

Tobias Pankrath:


But I would generally avoid SList.

It's not good.

And in general linked lists are quite overrated. On modern CPUs it's not easy to find situations where a linked list is better than a dynamic array or a chunked array (that is a dynamic array of pointers to arrays. It's often used to implement deques, etc).


Regarding arrays, maybe a StableVector will be good to have in Phobos:
http://www.boost.org/doc/libs/1_50_0_beta1/doc/html/container/non_standard_containers.html#container.non_standard_containers.stable_vector

Bye,
bearophile

I also think flat_map/_set from that same boost library would be great additions when the time comes to add new stuff to std.container.  Red black trees are kind of a niche container[1].  I replaced almost all of my usage of C++'s std::set and std::map with flat_map and flat_set and saw better memory use and far fewer cache misses (assuming I was reading the profiler correctly).  It was win-win since I rarely needed std::set/::map's exclusive features (iterator stability, log N insertions).  I believe I've read Andrei added something just like these to Loki.


[1] http://lafstern.org/matt/col1.pdf : Why you shouldn't use set (and what you should use instead)

Regards,
Brad Anderson
Dmitry Olshansky | 30 Jun 2012 15:15
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 30-Jun-12 15:35, Tobias Pankrath wrote:
>> Say I have a forward range (for example, an SList[]). I would like to
>> create a new range containing only the first 5 elements of that old
>> range. How can I do that?
>>
>
> std.algorithm.take.
>
> But I would generally avoid SList.

Indeed. I'd be hard pressed to devise realistic use case where it 
performs better.

That makes me think that a proper type people should be using is the 
unrolled list, that is a list of fixed-sized array chunks.

Or even chunked array like bearophile suggests.
--

-- 
Dmitry Olshansky

Andrei Alexandrescu | 30 Jun 2012 16:25

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 6/30/12 9:15 AM, Dmitry Olshansky wrote:
> On 30-Jun-12 15:35, Tobias Pankrath wrote:
>>> Say I have a forward range (for example, an SList[]). I would like to
>>> create a new range containing only the first 5 elements of that old
>>> range. How can I do that?
>>>
>>
>> std.algorithm.take.
>>
>> But I would generally avoid SList.
>
> Indeed. I'd be hard pressed to devise realistic use case where it
> performs better.

Singly-linked lists are frequently used with lock-free algorithms. 
Generally it's simplistic to compare slists with arrays as to "which 
performs better" because they have different primitives.

Andrei

Dmitry Olshansky | 1 Jul 2012 13:42
Picon

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 30-Jun-12 18:25, Andrei Alexandrescu wrote:
> On 6/30/12 9:15 AM, Dmitry Olshansky wrote:
>> On 30-Jun-12 15:35, Tobias Pankrath wrote:
>>>> Say I have a forward range (for example, an SList[]). I would like to
>>>> create a new range containing only the first 5 elements of that old
>>>> range. How can I do that?
>>>>
>>>
>>> std.algorithm.take.
>>>
>>> But I would generally avoid SList.
>>
>> Indeed. I'd be hard pressed to devise realistic use case where it
>> performs better.
>
> Singly-linked lists are frequently used with lock-free algorithms.

As post involved terms like "generally" I pointed out that indeed List 
should not be used "generally" :) Their value in certain scenarios is 
remarkable, or rather of linked storage such as in free lists, and (like 
you mentioned) certain lock-free algorithms.

Let's not forget however that what people seek is lock-free queues and 
other high-level abstractions. S-lists just happen to be a means to an 
end that works on current hardware.

I totally expect a new wave of simpler lock-free algorithms with things 
like Intel's TSX and something analogous that AMD will have.
(others would either copy or redesign similar things as it's an obvious 
need for the future multicores)

There a lot of links but this gives a nice overview and is fairly new:
http://arstechnica.com/business/2012/02/transactional-memory-going-mainstream-with-intel-haswell/
> Generally it's simplistic to compare slists with arrays as to "which
> performs better" because they have different primitives.

Agreed. Though it's not at all uncommon to mix things up in awful ways. 
*cough* Java *cough*


--

-- 
Dmitry Olshansky

Andrei Alexandrescu | 30 Jun 2012 16:18

Re: Creating a Sub-view of a non - RA (hasSlicing) range.

On 6/30/12 7:31 AM, monarch_dodra wrote:
> I've been enjoying my time with D's ranges, but something is nagging at
> me: If the range is not random access, then how does one create a
> sub-view of that range?

Use take or takeExactly.

> Say I have a forward range (for example, an SList[]). I would like to
> create a new range containing only the first 5 elements of that old
> range. How can I do that?

take(r, n) or takeExactly(r, n).

> I see no logical reason that prevents me from doing it, since C++
> iterators can do it quite fine.

Actually, not at all. Iterators are a lot worse off than ranges here.

C++'s std::forward_list (and generally any iterator-providing 
singly-linked list) only offers an iterator to the first element. (The 
"end" iterator is a wrapped null pointer. This is by definition of the 
structure - singly-linked lists don't track their last element.)

So if you were to to take 5 elements off a std::forward_list, you can't 
use lst.begin() and lst.begin() + 5 because there's no addition; you'd 
need to define a new iterator type that combines a forward iterator with 
an integer, and iterate using that. It's all painstaking revealing how 
much a range is better for such. This is because the range keeps 
together the iteration limits, whereas iterators insist on the fact that 
the iteration limits belong separately.

> (the C++ algorithms that work only on RA
> ranges is mostly for efficiency reasons). Not being able to do this
> would severely limit the amount of containers that can seamlessly merge
> with algorithms.
>
> For example, I can't call splitter on an SList. Not sure if this is just
> "not currently supported", or just not possible...
>
> Any thoughts?

It must be very well understood what the structural limits of 
singly-linked lists impose on their iteration. There's no way to take 5 
elements off a SList and pretend that's pretty much the same as taking 
the whole list.

We could build a design for using splitter with SLists only if the 
element type of splitter's result is different from SList's native range.

Andrei


Gmane