I'm not sure I'd want it for every list, but there are certain places it's nice.
[1] https://www.allaboutcircuits.com/technical-articles/circular...
Directly, it supports caches very well. You just increment the number of things you've ever cached and that's where your next cached value goes; you don't care when it overwrites an old value.
There are other cases where you just need some variant of a thing, but you don't actually care that much about which variant you get. You might want to vary your wording in auto-generated text, for instance, by rotating synonyms. Or rotating the tiles you use in a 2D game. In this case I'd define an interface where you pass in a "seed" integer and it gives you back some deterministic example; a circular array is the simplest implementation of this interface (but there are others).
You could also do simple load balancing by sending work to Worker[workCount++]. While usually you want to track each workers' existing workload (because the work takes unpredictable time), this simple approach could be sufficient if all your work completes in about the same time.
If you're doing fancy math or science computing, you may be working with finite groups or fields, whose elements you could stick in an N-dimensional circular array (based on the characteristics of the field).
Like:
- you can put a short chord sequence into a ring, and it now functions as a list of as many repetitions of that chord sequence as you like. You can just loop over it forever (which is kind of the essence of how sonic pi live-loop play works)
- you can put the notes that make up a scale into a ring, and use it to extract specific chords - like, take the 1st, 3rd, 5th, 7th and 9th note - from just a seven note scale.
- you can use rings of booleans to capture drum patterns and rings of notes to capture melodies, and loop them forever
- etc. etc.
Expression<Func<int, int>> lambda = n => n % 2;
Console.WriteLine(((dynamic)lambda).Body.NodeType);
Output is "Modulo".Ada has both rem and mod operatiors. I'm not sure how many other languages have operators for both.
I do think it's quite odd and frustrating that modulo can return negative numbers and I don't really get the reasoning there, but there's probably a good reason I don't know about.
For instance if you have an int array that contains the numbers 1-250 and you index with a uint8 variable i,
for (uint8 i = 247; i++;) {
// print circ_arr[i]
}
for the values of i near the overflow points of the circular array and of the uint8 it gets weird: i circ_arr[i]
247 247
248 248
249 249
250 250
251 1 # 251 % 250 = 0
252 2 # 252 % 250 = 1
253 3 # ...
254 4
255 5
0 1 # i overflows to 0
1 2
...If you're indexing with a [u]int32 you need to worry about this once every 4 billion increments, and if an incomplete cycle is a show-stopper for you, you can compute a safe modulo yourself based on the size(s) of your circular array(s), but more likely you just need something else. But really, you don't care if your cache hiccups a little once every 4 billion caches.
You make a good point, of course, I'm just allergic to people poking holes in back-of-the-napkin explanations of things with the trite "but integers can overflow!" It's one of those most common well actuallys written on this site. Of course integers can overflow. They almost never do though, do they? And if they do, a test fails and you add a single line somewhere to fix it.
I really think the vast majority of programmers are too often thinking about bits when they should be thinking about math.
You’d need a way to get that type, for example as
float a[10,20] // two-dimensional array of floats
typeof(a.dims(0)) i = 0 // modular type with values in [0,9]
typeof(a.dims(1)) j = 0 // modular type with values in [0,19]
or, slightly neater: auto i = a.indextype(0)
auto j = a.indextype(1)
Ugly syntax, but in a modern language, most code would probably do something like for (i,j,value) in a
where the types are inferred.Having those modular types means the compiler would do the arithmetic correct for the array, while the negative literals allow programmers to specify “last” and “next to last” correctly.
That's something I'd like in a bunch of languages - a real modulo operator that always returns between 0 and n, even for negative inputs, rather than a remainder operator that's advertised as a modulo operator. Grrrrr!!!!!
But, more importantly, it means that any custom collection type can define an indexer that can handle reverse indices in the manner that is appropriate for that particular collection; it's not just for arrays.
[1] https://learn.microsoft.com/en-us/dotnet/api/system.index
OTOH a convenience feature as a lock-in is hard to believe.
And it mihjt be possible to add a static method to array to index backwards yourself (Can't remember what they are called, but look and act like methods on the object but aren't).
And no, it's not possible to do this using an extension method, unfortunately - there are no extension properties or indexers in C# (yet; it's something that keeps coming up). But then again, if and when they add extension indexers, this arrangement with a custom type is what'd allow you to write one that does backwards indexing on a collection type that doesn't support it out of the box.
lst.reverse()[x]
which the compiler could guasrantee to recognise and simply implement as a calculation.And yes, of course, you can always do the same in some other, more verbose way. But why should we tolerate that verbosity when there's a solution that makes code both shorter and more readable? I rather hope that more languages will adopt one of these techniques.
> it silently does the wrong thing
yeah, my original complaint was this
> why should we tolerate that verbosity when there's a solution that makes code both shorter and more readable?
Because it's a balance. How much it benefits how many users to what degree vs. extra cost of implementation and maintenance. If you're not careful you go down the kitchen sink road and end up with bloat. Be careful when adding stuff cos you have to support it forever.
Anyway, thoughtful answers thanks.
foreach my $i (0..$#list) {
say "$i: $list[$i]";
}
For getting the last element from a list you can just use -1 (and of course further negative numbers work like you would expect, -2 is second to last and so on): my @last_three = @items[-1, -2, -3];EDIT: On the other hand, I think Matlab's array(end - number) indexing syntax is a good compromise of convenience and less error prone explicitness.
[1,2].at(-2) returns 1
Because an array with indexes [3, 7) has length 4, but 4 is not the index of the last element.
C/C++ doesn't have custom array indexes and as such <array[std::size(array) - 1]> is returning the last element of said array.
Delphi has custom array indexes and as such, taking your example with defining an array in the form <example_array : array[3..7] of integer>, I would not get the last element in case of <example_array[Length(example_array) - 1]. In this case I would have 2 options. Option 1 would be to use <High> function as in <example_array[High(example_array)]> to access example_array[7] element. Delphi also has <Low> function so you can iterate through a custom defined array by using <for> keyword with the help of them. Option 2 would be to actually build my own helper (this is the most wanted case when you're dealing with multi-dimensional arrays that also have custom indexes) and I would have something like <example_array.FromLastIndex(0)> to access example_array[7] element.
Hope this cleared the confusion.
for I in A'Range loop
A(I) = A(I) + A(I);
end loop;
Whatever the range is, this will work. If you really need the first and last elements or want to be explicit: Start := A'First;
End := A'Last;
And if the type of the range (since any discrete type can be used) doesn't support simple incrementing with +1 or similar, you can use 'Succ to step through: Index := A'First;
Index := Whatever_Type'Succ(Index);
Also 'Pred to work backwards. Those can be wrapped up in a simpler function if desired.Being able to give subarrays to a procedure and preventing buffer overruns everywhere, reducing screw-up scope everywhere is a superpower I didn't know I needed before starting writing proved parsers.
3 -> 0
4 -> 1
5 -> 2
6 -> 3
This works for vectors as well, so why not have a range from (0,0) to (5,5) to index into an array arr?
You could write the function that does the mapping manually: arr[(x,y)] = backing_array[x / 5 + y] //bounds checks omitted
But here it can be automated quite simply to allow for vectors of even 3 or 4 dimensions.Just know that custom indexes / ranges are not automagically broken. Personally, I like how much easier it is to read the intent with custom indices.
otherwise seems like an errors that are hard to spot.
> arr = ["a", "b", "c", "d", "e"]
> x = -2
> arr[x]
=> "d"
> Similar to the argument about signed/unsigned indices in low level languages.
Think that one has to do more with convenience where most of stuff uses int by default
This is exactly the difference between a language like PHP and a pure functional language. PHP says: usually we want to do X, but sometimes Y, so we'll make Z which does X unless Q is true in which case T1 will be set and Y will happen most of the time when you want it assuming you called it the write way and put an @ in the right spot otherwise P will happen because I hadn't had lunch when I wrote that and it seemed like P was pretty likely to be the case when T1 was set but an @ was not written but lately I've been feeling like maybe T2 should also be set sometimes so if you call Z and you want X but T1 is written and you don't want to write an @ then you can just set CONSTANT_FOO_BAR_WITHOUT_X_SET_AT to 17 because the other 16 codes are already used for other things.
Functional languages say: what if everything was just math?
TLDR: Not that weird. If it is something that is almost certainly going to fail code-review, then may as well let the compiler fail it.
Long:
Just because I want only literals allowed someplace, or only values allowed in other places is not even close to weird.
Most places, code review won't let a function call like `foo(true, false, true, false, true)` through, because the potential for errors is so high and the readability is low.
With this take I can see code review easily getting into the weeds for each `bar[x]` to determine if x will wrap around, while letting `bar[4]` through because it is clear it will not.
Right now, with most languages, we simply let `bar[x]` through because if it is out of bounds it will throw an error/panic/etc. I think it can only silently return wrong data in C and C++.
In this case everything is about intention
In general accessing index out of range (above or below) is not desirable, in almost all cases this is bug.
And now, in my opinion `array[-1]` when `-1` is hardcoded would tell, with full intention that last index is desired.
Basically it would be translated to `arr[arr.Length - 1]`. You don't write code with `array[-1]` because that's clearly wrong (when there's no going back behaviour)
Meanwhile when it is calculated, then it should result in an error.
The rules are pretty simple I'd say - if you desire to use "reverse syntax" then you can, but when you use variables with may be calculated wrongly, then you will receive an error.