www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Intended behavior of std.range.cycle?

reply "rcor" <murphyslaw480 gmail.com> writes:
auto c = cycle([1,2,3]);
foreach(i ; iota(-4,4)) {
   writeln(c[i]);
}

prints the sequence
1
2
3
1
1   <----- c[0] == c[-1]
2
3

I understand this is what would happen if I were to just use 
modulus on an index to access the original array, but should 
Cycle really mimic this behavior? I feel like most uses of Cycle 
would expect element -1 to be the last element of the original 
range, not the first. I could manually apply addition with 
modulus to ensure that the index is always positive, but then 
there's not much benefit to using cycle anyways -- I might as 
well be accessing the original range.
Is this behavior intentional or an oversight?
Sep 04 2014
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 September 2014 at 11:29:30 UTC, rcor wrote:
 auto c = cycle([1,2,3]);
 foreach(i ; iota(-4,4)) {
   writeln(c[i]);
 }

 prints the sequence
 1
 2
 3
 1
 1   <----- c[0] == c[-1]
 2
 3

 I understand this is what would happen if I were to just use 
 modulus on an index to access the original array, but should 
 Cycle really mimic this behavior? I feel like most uses of 
 Cycle would expect element -1 to be the last element of the 
 original range, not the first. I could manually apply addition 
 with modulus to ensure that the index is always positive, but 
 then there's not much benefit to using cycle anyways -- I might 
 as well be accessing the original range.
 Is this behavior intentional or an oversight?
Indexing is done with the unsigned size_t. You aren't indexing at "-1", but rather, size_t.max. size_t.max % 3 probably doesn't result in "3 - 1" hence what you are observing. *Should* cycle be negatively index-able? Personally, I don't think so. And even if it could, it has been proven non-size_t indexing is not well supported at all. It was de-facto chosen after the "iota-map fiasco" that all ranges be indexed with size_t and nothing else...
Sep 04 2014
next sibling parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Thursday, 4 September 2014 at 11:43:28 UTC, monarch_dodra 
wrote:
 Indexing is done with the unsigned size_t.
I re-read your post, and I don't think I actually answered your question...? I don't know of any case where you'd want to index negativelly. That said, I'm sure it could be possible to wrap a cycle into a simple "signed cycle" adaptor. There's one issue with what you want to do though: Who is front, and where does your range start?
Sep 04 2014
parent reply "rcor" <murphyslaw480 gmail.com> writes:
On Thursday, 4 September 2014 at 11:58:58 UTC, monarch_dodra 
wrote:
 On Thursday, 4 September 2014 at 11:43:28 UTC, monarch_dodra 
 wrote:
 Indexing is done with the unsigned size_t.
I re-read your post, and I don't think I actually answered your question...? I don't know of any case where you'd want to index negativelly. That said, I'm sure it could be possible to wrap a cycle into a simple "signed cycle" adaptor. There's one issue with what you want to do though: Who is front, and where does your range start?
To give more detail: I've been working on a strategy game. The player has 'next' and 'previous' buttons they can use to cycle between units they haven't moved yet. I thought it would be cleaner to use a Cycle than to have the extra math necessary to wrap the index (its not that the math is terribly complicated, but it just makes things messier and less obvious). In this case, "front" is arbitrarily chosen based on the order in which the units were loaded. It doesn't really matter who is front as long as each press of 'next' or 'previous' jumps to a new unit. The "negative" index occurs when the player has unit 0 selected and presses 'previous'. Instead of jumping to the previous unit, the cursor sticks on the same unit (at least in the situation where size_t.max % numUnits == 0). It's not a huge deal to work around it in my own code, but I figured I should point it out in case this was unintentional.
Sep 04 2014
parent "rcor" <murphyslaw480 gmail.com> writes:
A closer look shows that cycle "repeats the given _forward_ range 
indefinitely", so I guess the current behavior is at least 
consistent with the docs.
Sep 04 2014
prev sibling parent reply "Vlad Levenfeld" <vlevenfeld gmail.com> writes:
On Thursday, 4 September 2014 at 11:43:28 UTC, monarch_dodra 
wrote:
 *Should* cycle be negatively index-able? Personally, I don't 
 think so. And even if it could, it has been proven non-size_t 
 indexing is not well supported at all. It was de-facto chosen 
 after the "iota-map fiasco" that all ranges be indexed with 
 size_t and nothing else...
Can you elaborate on what the problem was? I'm curious as I've recently implemented a DSP module that uses ranges with floating-point slicing and, other than having to define a second kind of length (I call it "measure" so I can still have a discrete length when I need it), it seems to work well enough so far. It'd be bad if there were some unseen fiasco waiting for me...
Sep 05 2014
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Friday, 5 September 2014 at 10:41:22 UTC, Vlad Levenfeld wrote:
 On Thursday, 4 September 2014 at 11:43:28 UTC, monarch_dodra 
 wrote:
 *Should* cycle be negatively index-able? Personally, I don't 
 think so. And even if it could, it has been proven non-size_t 
 indexing is not well supported at all. It was de-facto chosen 
 after the "iota-map fiasco" that all ranges be indexed with 
 size_t and nothing else...
Can you elaborate on what the problem was? I'm curious as I've recently implemented a DSP module that uses ranges with floating-point slicing and, other than having to define a second kind of length (I call it "measure" so I can still have a discrete length when I need it), it seems to work well enough so far. It'd be bad if there were some unseen fiasco waiting for me...
In itself, there is nothing wrong with having a "shallow container" that can be indexed from -inf to +inf. The "issues" might appear if you try to tag on the "range" interface on it. In particular, who is "front"? If I do a "foreach" over the sequence, then will it miss half the elements? What does it mean to slice from -5 to +5 ? Which element becomes the "new" 0? As for floating point indexing, it's the same thing. The indexing itself is fine, there's no problem there. But if you popFront, how many elements are you actually skipping over. What would it mean if I were to "take(4.5)" elements? *Overall*, it should be fine, but if you are creative, you could hurt yourself. I don't know your exact use case though. So my conclusion is mostly that as long as you only index, you should be fine. Mix in range adaptors, and things *could* become less stable. *could*. But you should still be mostly safe. Again, depends on use case.
Sep 06 2014