109

I need to move backwards through an array, so I have code like this:

for (int i = myArray.Length - 1; i >= 0; i--)
{
    // Do something
    myArray[i] = 42;
}

Is there a better way of doing this?

Update: I was hoping that maybe C# had some built-in mechanism for this like:

foreachbackwards (int i in myArray)
{
    // so easy
}
Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • 5
    Most of this question is sort of a review of the answers below. Suggest it be shortened significantly. – einpoklum Nov 06 '13 at 11:12
  • 1
    I'm not sure I see why any of the alternatives is better, if goodness includes clarity or maintainability. – dkretz Nov 09 '08 at 17:44
  • True. I was hoping to find a more reasonable way to do this, since I have to do it fairly often. – MusiGenesis Nov 09 '08 at 18:04
  • 2
    Please remove C and C++ from the title and hashtags. – Mariusz Jaskółka Jul 31 '20 at 06:23
  • Removing the C and C++ from the title and the tags, would invalidate half a dozen of legitimate answers. – Theodor Zoulias Oct 12 '22 at 08:06
  • 3
    This question was closed after meta discussion: https://meta.stackoverflow.com/questions/424652/question-is-tagged-c-c-and-c-no-answer-explains-all-three-languages?noredirect=1#comment957425_424652 – VLL May 11 '23 at 09:04

18 Answers18

181

While admittedly a bit obscure, I would say that the most typographically pleasing way of doing this is

for (int i = myArray.Length; i --> 0; )
{
    //do something
}
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • 42
    When I first read your answer it looked like it wouldn't even compile, so I assumed you were a crazy person. But it's exactly what I was looking for: a better way of writing a backwards for loop. – MusiGenesis Nov 10 '08 at 03:59
  • 4
    I think that i --> 0; is on purpose. That's what he means by "typographically pleasing" – Johannes Schaub - litb Nov 10 '08 at 04:08
  • 24
    I found it "typographically confusing" myself. For me, the "--" doesn't look right unless it's adjacent to the variable it's affecting. – MusiGenesis Nov 10 '08 at 04:11
  • 1
    I changed his version back. It does look better esthetically that way, but it also looks more like it isn't even valid code. – MusiGenesis Nov 10 '08 at 04:19
  • That's pretty cool - Not sure if it would be a good idea to use, but still quite cool – hugoware Nov 13 '08 at 14:16
  • I prefer this: `for (int i = myArray.Length; i-->0 ;)` - I think that would score one extra WTF point. – Hamish Grubijan Aug 10 '10 at 01:47
  • @Hamish unfortunately, that will cause a segfault when you try to access the array[i] and it hits the memory location immediately after the end of the array. If it didn't segfault, at worst you'd never hit the first element in the array, since `0 > 0 == false`. – corsiKa Jun 26 '11 at 00:29
  • @corsiKa: It will never access myArray[i]. – Marcelo Cantos Mar 14 '12 at 00:51
  • 7
    This is cute, but cute ≠ good style. Anything that slows down the read is a bad idea. Plus, it's more typing than just `for (int i = myArray.Length; i--;) { … }`. – Marcelo Cantos Mar 14 '12 at 00:54
  • 39
    That's too obscure and obfuscated. I'd never write something like this in production code... – Mihai Todor Jun 22 '12 at 13:04
  • That is *so misleading*. I thought it was another operator. [It's not.](http://stackoverflow.com/a/1642035/1708136) Cool, though. – Meredith Nov 18 '13 at 01:52
  • 11
    Aah the [goes to operator (-->)](http://stackoverflow.com/questions/1642028/what-is-the-name-of-this-operator?lq=1) does the trick! – nawfal Jan 10 '14 at 12:59
  • 2
    `for (int i = myArray.Length; i-- > 0; )` There, I fixed it. – Thomas Eding Apr 23 '15 at 16:16
  • 3
    Its not obscure, its thr only correct thing to do (after changing int to size_t). – lalala Nov 06 '19 at 19:56
  • While `i` approaches `0` i.e.: `i --> 0`; functionally the same as `(i = i - 1) + 1 > 0`. That's beautiful @Rune. – Lapys Jan 05 '20 at 03:52
  • Shouldn't myArray.Length be "myArray.Length -1" since array indexes start at zero? – PastExpiry.com Jan 14 '20 at 15:16
  • @PastExpiry.com no, because the first comparison (and thus decrementation) step is done before the first execution of the loop body. – Ruslan Sep 10 '20 at 06:57
  • 1
    "typographically pleasing" is nonsense. The "arrow operator" sorts under the category of posing: writing needlessly obscure code for the sake of showing off knowledge of some language details. Writing crap like this in a professional context is an excellent attempt of getting oneself fired from the programmer job. – Lundin Oct 12 '22 at 08:12
121

In C++ you basicially have the choice between iterating using iterators, or indices. Depending on whether you have a plain array, or a std::vector, you use different techniques.

Using std::vector

Using iterators

C++ allows you to do this using std::reverse_iterator:
for(std::vector<T>::reverse_iterator it = v.rbegin(); it != v.rend(); ++it) {
    /* std::cout << *it; ... */
}

Using indices

The unsigned integral type returned by `std::vector::size` is *not* always `std::size_t`. It can be greater or less. This is crucial for the loop to work.
for(std::vector<int>::size_type i = someVector.size() - 1; 
    i != (std::vector<int>::size_type) -1; i--) {
    /* std::cout << someVector[i]; ... */
}

It works, since unsigned integral types values are defined by means of modulo their count of bits. Thus, if you are setting -N, you end up at (2 ^ BIT_SIZE) -N

Using Arrays

Using iterators

We are using `std::reverse_iterator` to do the iterating.
for(std::reverse_iterator<element_type*> it(a + sizeof a / sizeof *a), itb(a); 
    it != itb; 
    ++it) {
    /* std::cout << *it; .... */
}

Using indices

We can safely use `std::size_t` here, as opposed to above, since `sizeof` always returns `std::size_t` by definition.
for(std::size_t i = (sizeof a / sizeof *a) - 1; i != (std::size_t) -1; i--) {
   /* std::cout << a[i]; ... */
}

Avoiding pitfalls with sizeof applied to pointers

Actually the above way of determining the size of an array sucks. If a is actually a pointer instead of an array (which happens quite often, and beginners will confuse it), it will silently fail. A better way is to use the following, which will fail at compile time, if given a pointer:
template<typename T, std::size_t N> char (& array_size(T(&)[N]) )[N];

It works by getting the size of the passed array first, and then declaring to return a reference to an array of type char of the same size. char is defined to have sizeof of: 1. So the returned array will have a sizeof of: N * 1, which is what we are looking for, with only compile time evaluation and zero runtime overhead.

Instead of doing

(sizeof a / sizeof *a)

Change your code so that it now does

(sizeof array_size(a))
Donald Duck
  • 8,409
  • 22
  • 75
  • 99
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Additionally, if your container doesn't have a reverse iterator, you can use boost's reverse_iterator implementation: http://www.boost.org/doc/libs/1_36_0/libs/iterator/doc/reverse_iterator.html – MP24 Nov 09 '08 at 17:24
  • your array_size seems to work for statically allocated arrays, but failed for me when a was 'new int[7]' for example. – Nate Parsons Dec 29 '08 at 05:12
  • 2
    yeah that is the intent of it :) new int[7] returns a pointer. so sizeof(new int[7]) returns not 7 * sizeof(int) , but would return sizeof(int*) . array_size causes a compile error for that case instead of silently working. – Johannes Schaub - litb Dec 29 '08 at 05:22
  • also try array_size(+statically_allocated_array) , which also fails, because the + operator makes the array into a pointer to its first element. using plain sizeof would give you the size of a pointer again – Johannes Schaub - litb Dec 29 '08 at 05:23
  • For the first thing - why not just use `end` and `begin` in reverse order? – Tomáš Zato Jan 06 '16 at 15:56
  • 1
    `for (auto i = someVector.size(); i--;)` is simpler. – L. F. Jan 10 '20 at 12:34
63

I would always prefer clear code against 'typographically pleasing' code. Thus, I would always use :

for (int i = myArray.Length - 1; i >= 0; i--)  
{  
    // Do something ...  
}    

You can consider it as the standard way to loop backwards.
Just my two cents...

Jack Griffin
  • 1,228
  • 10
  • 17
  • Worked. Have not done this in C# yet, but thank you. – PCPGMR Oct 15 '14 at 15:22
  • So how if the array is really large(so the index has to be of unsigned type)? size_t is actually unsigned, isnt it? – lalala Nov 06 '19 at 19:54
  • You can declare i as uint.See Marc Gravell's post. – Jack Griffin Nov 10 '19 at 16:57
  • 4
    Won't work for an unsigned type due to wrap, unlike the typographically pleasing one. Not good. – Ocelot Jul 22 '20 at 12:23
  • @lalala You can use a `long long`. That has at least 64 bits, so you're array can have as much as 2^63 entries. That should do for a while... – Elmar Zander Jan 13 '21 at 16:12
  • @Ocelot Writing down-counting loops using an unsigned iterator is a bad idea to begin with. The problem lies with the choice of variable type, not the implementation. In the extremely rare scenario where you _must_ use an unsigned iterator, you can do an up-counting loop and compensate by doing `i - size - 1` inside the loop body. Not the prettiest, but bad ideas often lead to ugly code. – Lundin Oct 12 '22 at 08:24
  • 1
    @Lundin please explain why you think that using an unsigned iterator is a bad idea. – Ocelot Oct 13 '22 at 14:39
  • @Ocelot What I said was "Writing _down-counting loops_ using an unsigned iterator is a bad idea". Up-counting loops most commonly have an unsigned type like `size_t`. And the reason why it's a bad idea for down-counting loops is obvious: it can never get a value lower than zero, so there's a big potential for wrap-around bugs. – Lundin Oct 14 '22 at 13:06
  • 1
    @Lundin there is no problem with wrap-around when using "for(iter; iter-->0;)" version. Without anything else to back it up, your initial statement is only your personal opinion. – Ocelot Oct 31 '22 at 03:00
  • @Ocelot The problem with that one is different, namely code obfuscation. As for sources, see for example MISRA C Rule 14.2. What's _your_ rationale/sources for using the "arrow operator"? – Lundin Oct 31 '22 at 07:20
  • 1
    @Lundin oh god, MISRA... As for my rationale, well, unsigned types. – Ocelot Oct 31 '22 at 11:09
  • @Ocelot Without anything else to back it up, it's just your personal opinion... – Lundin Oct 31 '22 at 11:22
  • @Lundin for starters, MISRA wikipedia article has a whole section dedicated to criticism where it cites research on its effectiveness, MISRA is also known itself as a cause of uglier code which is evident by users using workarounds against some "stupid rules". As for unsigned types, "--> operator" is the only one to work as intended, it's a fact. – Ocelot Nov 01 '22 at 03:54
  • @Ocelot oh god, Wikipedia... "--> operator" is the only one to work as intended, it's a fact" Without anything else to back it up, it's just your personal opinion. – Lundin Nov 01 '22 at 07:27
  • (As for the criticism on Wikipedia, it mainly refers to papers by Les Hatton, who was a pioneer in safe subset coding standards for C in the 90s. He presented valid criticism which was mainly about a lot of the rules leading to a lot of false positives. This remains a problem to this day - the major cause is the many tool vendors who sell broken tools at high prices. Then charge you even more to fix the bugs in the tool they incorrectly claimed was suitable for MISRA compliance testing. There needs to be a MISRA certification practice to end all the quackery in the tool branch.) – Lundin Nov 01 '22 at 07:36
  • @Ocelot I gave the alternative in the very first comment above. Essentially it keeps the 3 clauses of `for` clean and readable at the cost of more complex code in the loop body. As for performance, I'll pass from making an argument regarding if up-counting is more cache-friendly than down-counting, but it is at least not less efficient. – Lundin Nov 01 '22 at 08:44
  • @Lundin sorry this may sound way too pedantic, it's an alternative all right, but it's not a backwards loop. I should have clarified that the iterator must go backwards, that one is on me. By the way, this very situation with a workaround for the backwards loop is itself an argument not in favor of MISRA. – Ocelot Nov 01 '22 at 09:24
  • @Ocelot Iterating upwards or backwards is not a goal in itself, it's just some means to achieve a certain goal = whatever the loop is supposed to result in. There may be multiple ways of achieving it, including something else than what the C code says entirely, carried out by the optimizing compiler when generating the actual machine code. – Lundin Nov 01 '22 at 10:21
56

In C#, using Visual Studio 2005 or later, type 'forr' and hit [TAB] [TAB]. This will expand to a for loop that goes backwards through a collection.

It's so easy to get wrong (at least for me), that I thought putting this snippet in would be a good idea.

That said, I like Array.Reverse() / Enumerable.Reverse() and then iterate forwards better - they more clearly state intent.

MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
17

In C# using Linq:

foreach(var item in myArray.Reverse())
{
    // do something
}
Keltex
  • 26,220
  • 11
  • 79
  • 111
  • This is for sure the simplest, but note that in the current version of the CLR reversing a list is always an O(n) operation rather than the O(1) operation it could be if it is an IList; see http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355096 – Greg Beech Nov 10 '08 at 01:20
  • 2
    Looks like .Reverse() makes a local copy of the array and then iterates backwards through it. It seems kind of inefficient to copy an array just so you can iterate through it. I guess any post with "Linq" in it is worthy of up-votes. :) – MusiGenesis Nov 10 '08 at 03:07
  • There is a possible tradeoff, but then you run into the issue that the "voted on answer" (i --> 0) might result in slower code because (i) has to be checked against the size of the array each time it is used as in index. – Keltex Nov 10 '08 at 20:16
  • 1
    While I agree that Linq is great, the problem with this approach is that an iterator doesn't allow you to selectively pick out items of the array - consider the situation where you wanted to navigate through a portion of an Array, but not the entire thing... – jesses.co.tt Feb 17 '16 at 21:55
12

That's definitely the best way for any array whose length is a signed integral type. For arrays whose lengths are an unsigned integral type (e.g. an std::vector in C++), then you need to modify the end condition slightly:

for(size_t i = myArray.size() - 1; i != (size_t)-1; i--)
    // blah

If you just said i >= 0, this is always true for an unsigned integer, so the loop will be an infinite loop.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • In C++ the "i--" would automatically wrap around to the highest value if i was 0? Sorry, I'm C++ impaired. – MusiGenesis Nov 09 '08 at 15:19
  • Amazing. You guys just helped me understand the cause of a hard-drive filling bug in something I wrote 12 years ago. Good thing I don't work in C++. – MusiGenesis Nov 09 '08 at 15:34
  • termination condition should be: i < myArray.size(). Only depends on overflow properties of unsigned int; not implementation details of integers and casts. Consequently easier to read and works in languages with alternate integer representations. – ejgottl Nov 09 '08 at 15:39
  • I think a better solution for me is to stay in the C# world where I can't hurt anybody. – MusiGenesis Nov 09 '08 at 15:45
4

Looks good to me. If the indexer was unsigned (uint etc), you might have to take that into account. Call me lazy, but in that (unsigned) case, I might just use a counter-variable:

uint pos = arr.Length;
for(uint i = 0; i < arr.Length ; i++)
{
    arr[--pos] = 42;
}

(actually, even here you'd need to be careful of cases like arr.Length = uint.MaxValue... maybe a != somewhere... of course, that is a very unlikely case!)

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
3

In C I like to do this:


int i = myArray.Length;
while (i--) {
  myArray[i] = 42;
}

C# example added by MusiGenesis:

{int i = myArray.Length; while (i-- > 0)
{
    myArray[i] = 42;
}}
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
Twotymz
  • 412
  • 4
  • 6
  • I like this. It took me a second or two to realize that your method works. Hope you don't mind the added C# version (the extra braces are so that the index is scoped the same as in a for loop). – MusiGenesis Nov 10 '08 at 03:37
  • I'm not sure I would use this in production code, because it would elicit a universal "WTF is this?" reaction from whoever had to maintain it, but it's actually easier to type than a normal for loop, and no minus signs or >= symbols. – MusiGenesis Nov 10 '08 at 03:42
  • 1
    Too bad the C# version needs the extra " > 0 ". – MusiGenesis Nov 10 '08 at 03:44
  • I'm not sure what language it is. but your first code snippet is certainly NOT C :) just to tell you the obvious. maybe you wanted to write C# and html failed to parse it or something? – Johannes Schaub - litb Nov 13 '08 at 15:17
  • @litb: I guess the "myArray.Length" isn't valid C (?), but the while loop part works and looks great. – MusiGenesis Nov 14 '08 at 03:30
  • Oops. Your correct the initialization of i is not C. I was just trying to use the OP original snippet to illustrate the point. – Twotymz Nov 15 '08 at 15:16
  • It's looks great, but I do agree it disconcerts you at first glance. – Martín Coll Jul 31 '12 at 13:35
3

The best way to do that in C++ is probably to use iterator (or better, range) adaptors, which will lazily transform the sequence as it is being traversed.

Basically,

vector<value_type> range;
foreach(value_type v, range | reversed)
    cout << v;

Displays the range "range" (here, it's empty, but i'm fairly sure you can add elements yourself) in reverse order. Of course simply iterating the range is not much use, but passing that new range to algorithms and stuff is pretty cool.

This mechanism can also be used for much more powerful uses:

range | transformed(f) | filtered(p) | reversed

Will lazily compute the range "range", where function "f" is applied to all elements, elements for which "p" is not true are removed, and finally the resulting range is reversed.

Pipe syntax is the most readable IMO, given it's infix. The Boost.Range library update pending review implements this, but it's pretty simple to do it yourself also. It's even more cool with a lambda DSEL to generate the function f and the predicate p in-line.

2

I prefer a while loop. It's more clear to me than decrementing i in the condition of a for loop

int i = arrayLength;
while(i)
{
    i--;
    //do something with array[i]
}
Petko Petkov
  • 324
  • 3
  • 6
1

i do this

if (list.Count > 0)
    for (size_t i = list.Count - 1; ; i--)
    {
        //do your thing
    
        if (i == 0) //for preventing unsigned wrap
            break;
    }

but for some reason visual studio 2019 gets angry and warns me "ill-defined loop" or something.. it doesnt trust me


edit: you can remove "i >= 0" from "for (size_t i = list.Count - 1; i >= 0; i--)" .. its unnecessary

Ibrahim Ozdemir
  • 613
  • 1
  • 5
  • 18
  • 1
    `size_t i` is always >= 0. "but for some reason visual studio 2019 gets angry and warns me" That's because you are writing bugs. Listen to the compiler next time. Sure the break is a bug fix, but it would be better to not have such dirty bug fixes to begin with. What if someone adds code before/after the break for example, then how many times are you actually iterating? As many as expected? One more? One less? – Lundin Oct 12 '22 at 08:17
  • 1
    You don't have to be a "noob" to trip and fail during code maintenance of exotic loops. Bugs appearing during maintenance are often caused by the original programmer writing needlessly obscure code. Removing the 2nd clause like you did just now is better as it silences the compiler, but I would probably have considered a while loop at that point. Or a less exotic for loop. – Lundin Oct 13 '22 at 14:00
0

I'm going to try answering my own question here, but I don't really like this, either:

for (int i = 0; i < myArray.Length; i++)
{
    int iBackwards = myArray.Length - 1 - i; // ugh
    myArray[iBackwards] = 666;
}
MusiGenesis
  • 74,184
  • 40
  • 190
  • 334
  • Rather than do the .Length - 1 - i each time, perhaps consider a second variable? See my [updated] post. – Marc Gravell Nov 09 '08 at 15:21
  • Down-voted on my own question. Harsh. It's not any clunkier than the original. – MusiGenesis Nov 09 '08 at 17:58
  • This is actually one of the better answers. It's always preferable to count upwards and keep the 3 for clauses as readable as possible. Moving the obscurity out of the for clauses and into the loop body is pretty sound. – Lundin Oct 12 '22 at 08:21
0

I'd use the code in the original question, but if you really wanted to use foreach and have an integer index in C#:

foreach (int i in Enumerable.Range(0, myArray.Length).Reverse())
{
    myArray[i] = 42; 
}
xyz
  • 27,223
  • 29
  • 105
  • 125
0
// this is how I always do it
for (i = n; --i >= 0;){
   ...
}
Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
0

For C++:

As mentioned by others, when possible (i.e. when you only want each element at a time) it is strongly preferable to use iterators to both be explicit and avoid common pitfalls. Modern C++ has a more concise syntax for that with auto:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    std::cout<<*it<<" ";
}

prints 4 3 2 1 .

You can also modify the value during the loop:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.rend(); ++it) {
    *it = *it + 10;
    std::cout<<*it<<" ";
}

leading to 14 13 12 11 being printed and {11, 12, 13, 14} being in the std::vector afterwards.

If you don't plan on modifying the value during the loop, you should make sure that you get an error when you try to do that by accident, similarly to how one might write for(const auto& element : vec). This is possible like this:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.crbegin(); it != vec.crend(); ++it) { // used crbegin()/crend() here...
    *it = *it + 10; // ... so that this is a compile-time error
    std::cout<<*it<<" ";
}

The compiler error in this case for me is:

/tmp/main.cpp:20:9: error: assignment of read-only location ‘it.std::reverse_iterator<__gnu_cxx::__normal_iterator<const int*, std::vector<int> > >::operator*()’
   20 |     *it = *it + 10;
      |     ~~~~^~~~~~~~~~

Also note that you should make sure not to use different iterator types together:

std::vector<int> vec = {1,2,3,4};
for (auto it = vec.rbegin(); it != vec.end(); ++it) { // mixed rbegin() and end()
    std::cout<<*it<<" ";
}

leads to the verbose error:

/tmp/main.cpp: In function ‘int main()’:
/tmp/main.cpp:19:33: error: no match for ‘operator!=’ (operand types are ‘std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >’ and ‘std::vector<int>::iterator’ {aka ‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’})
   19 | for (auto it = vec.rbegin(); it != vec.end(); ++it) {
      |                              ~~ ^~ ~~~~~~~~~
      |                              |            |
      |                              |            std::vector<int>::iterator {aka __gnu_cxx::__normal_iterator<int*, std::vector<int> >}
      |                              std::reverse_iterator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > >

If you have C-style arrays on the stack, you can do things like this:

int vec[] = {1,2,3,4};
for (auto it = std::crbegin(vec); it != std::crend(vec); ++it) {
    std::cout<<*it<<" ";
}

If you really need the index, consider the following options:

  • check the range, then work with signed values, e.g.:
void loop_reverse(std::vector<int>& vec) {
    if (vec.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
        throw std::invalid_argument("Input too large");
    }
    const int sz = static_cast<int>(vec.size());
    for(int i=sz-1; i >= 0; --i) {
        // do something with i
    }
}
  • Work with unsigned values, be careful, and add comments, e.g.:
void loop_reverse2(std::vector<int>& vec) {
    for(size_t i=vec.size(); i-- > 0;) { // reverse indices from N-1 to 0
        // do something with i
    }
}
  • calculate the actual index separately, e.g.:
void loop_reverse3(std::vector<int>& vec) {
    for(size_t offset=0; offset < vec.size(); ++offset) {
        const size_t i = vec.size()-1-offset; // reverse indices from N-1 to 0
        // do something with i
    }
}
nspo
  • 1,488
  • 16
  • 21
0

If you use C++ and want to use size_t, not int,

for (size_t i = yourVector.size(); i--;) {
    // i is the index.
}

(Note that -1 is interpreted as a large positive number if it's size_t, thus a typical for-loop such as for (int i = yourVector.size()-1; i>=0; --i) doesn't work if size_t is used instead of int.)

starriet
  • 2,565
  • 22
  • 23
-1

Not that it matters after 13+ years but just for educational purposes and a bit of trivial learning;

The original code was;

for (int i = myArray.Length - 1; i >= 0; i--)
{
    // Do something
    myArray[i] = 42;
}

You don't really need to test 'i' again being greater or equal to zero since you simply need to only produce a 'false' result to terminate the loop. Therefore, you can simple do this where you are only testing 'i' itself if it is true or false since it will be (implicitly) false when it hits zero.;

for (int i = myArray.Length - 1; i; i--)
{
    // Do something
    myArray[i] = 42;
}

Like I stated, it doesn't really matter, but it is just interesting to understand the mechanics of what is going on inside the for() loop.

  • This actually doesn't work because your loop breaks when `i` is 0, but the original loop doesn't break until `i` is -1. The loop you propose will leave `myArray[0]` uninitialized – Willis Hershey Oct 20 '22 at 20:31
  • If you prefer to simply use `i` as the boolean expression, you could write `for(int i = myArray.Length; i; i--) myArray[i-1] = 42;` and that would work, but I would argue that the purpose of the original loop is more clear and easy to understand. – Willis Hershey Oct 20 '22 at 20:33
  • Thanks Willis Hershey for pointing that out, a slight error on my part. – RecursiveDude Oct 22 '22 at 07:45
  • You're welcome, but it's not a slight error, it's a rather serious bug, and you should edit your answer – Willis Hershey Oct 24 '22 at 12:55
-5

NOTE: This post ended up being far more detailed and therefore off topic, I apologize.

That being said my peers read it and believe it is valuable 'somewhere'. This thread is not the place. I would appreciate your feedback on where this should go (I am new to the site).


Anyway this is the C# version in .NET 3.5 which is amazing in that it works on any collection type using the defined semantics. This is a default measure (reuse!) not performance or CPU cycle minimization in most common dev scenario although that never seems to be what happens in the real world (premature optimization).

*** Extension method working over any collection type and taking an action delegate expecting a single value of the type, all executed over each item in reverse **

Requres 3.5:

public static void PerformOverReversed<T>(this IEnumerable<T> sequenceToReverse, Action<T> doForEachReversed)
      {
          foreach (var contextItem in sequenceToReverse.Reverse())
              doForEachReversed(contextItem);
      }

Older .NET versions or do you want to understand Linq internals better? Read on.. Or not..

ASSUMPTION: In the .NET type system the Array type inherits from the IEnumerable interface (not the generic IEnumerable only IEnumerable).

This is all you need to iterate from beginning to end, however you want to move in the opposite direction. As IEnumerable works on Array of type 'object' any type is valid,

CRITICAL MEASURE: We assume if you can process any sequence in reverse order that is 'better' then only being able to do it on integers.

Solution a for .NET CLR 2.0-3.0:

Description: We will accept any IEnumerable implementing instance with the mandate that each instance it contains is of the same type. So if we recieve an array the entire array contains instances of type X. If any other instances are of a type !=X an exception is thrown:

A singleton service:

public class ReverserService { private ReverserService() { }

    /// <summary>
    /// Most importantly uses yield command for efficiency
    /// </summary>
    /// <param name="enumerableInstance"></param>
    /// <returns></returns>
    public static IEnumerable ToReveresed(IEnumerable enumerableInstance)
    {
        if (enumerableInstance == null)
        {
            throw new ArgumentNullException("enumerableInstance");
        }

        // First we need to move forwarad and create a temp
        // copy of a type that allows us to move backwards
        // We can use ArrayList for this as the concrete
        // type

        IList reversedEnumerable = new ArrayList();
        IEnumerator tempEnumerator = enumerableInstance.GetEnumerator();

        while (tempEnumerator.MoveNext())
        {
            reversedEnumerable.Add(tempEnumerator.Current);
        }

        // Now we do the standard reverse over this using yield to return
        // the result
        // NOTE: This is an immutable result by design. That is 
        // a design goal for this simple question as well as most other set related 
        // requirements, which is why Linq results are immutable for example
        // In fact this is foundational code to understand Linq

        for (var i = reversedEnumerable.Count - 1; i >= 0; i--)
        {
            yield return reversedEnumerable[i];
        }
    }
}



public static class ExtensionMethods
{

      public static IEnumerable ToReveresed(this IEnumerable enumerableInstance)
      {
          return ReverserService.ToReveresed(enumerableInstance);
      }
 }

[TestFixture] public class Testing123 {

    /// <summary>
    /// .NET 1.1 CLR
    /// </summary>
    [Test]
    public void Tester_fornet_1_dot_1()
    {
        const int initialSize = 1000;

        // Create the baseline data
        int[] myArray = new int[initialSize];

        for (var i = 0; i < initialSize; i++)
        {
            myArray[i] = i + 1;
        }

        IEnumerable _revered = ReverserService.ToReveresed(myArray);

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));
    }

    [Test]
    public void tester_why_this_is_good()
    {

        ArrayList names = new ArrayList();
        names.Add("Jim");
        names.Add("Bob");
        names.Add("Eric");
        names.Add("Sam");

        IEnumerable _revered = ReverserService.ToReveresed(names);

        Assert.IsTrue(TestAndGetResult(_revered).Equals("Sam"));


    }

    [Test]
    public void tester_extension_method()
  {

        // Extension Methods No Linq (Linq does this for you as I will show)
        var enumerableOfInt = Enumerable.Range(1, 1000);

        // Use Extension Method - which simply wraps older clr code
        IEnumerable _revered = enumerableOfInt.ToReveresed();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }


    [Test]
    public void tester_linq_3_dot_5_clr()
    {

        // Extension Methods No Linq (Linq does this for you as I will show)
        IEnumerable enumerableOfInt = Enumerable.Range(1, 1000);

        // Reverse is Linq (which is are extension methods off IEnumerable<T>
        // Note you must case IEnumerable (non generic) using OfType or Cast
        IEnumerable _revered = enumerableOfInt.Cast<int>().Reverse();

        Assert.IsTrue(TestAndGetResult(_revered).Equals(1000));


    }



    [Test]
    public void tester_final_and_recommended_colution()
    {

        var enumerableOfInt = Enumerable.Range(1, 1000);
        enumerableOfInt.PerformOverReversed(i => Debug.WriteLine(i));

    }



    private static object TestAndGetResult(IEnumerable enumerableIn)
    {
      //  IEnumerable x = ReverserService.ToReveresed(names);

        Assert.IsTrue(enumerableIn != null);
        IEnumerator _test = enumerableIn.GetEnumerator();

        // Move to first
        Assert.IsTrue(_test.MoveNext());
        return _test.Current;
    }
}