11

I have found the following loop annotation in a big project I am working on (pseudocode):

var someOtherArray = [];
for (var i = 0, n = array.length; i < n; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

What brought my attention is this extra "n" variable. I have never seen a for lop written in this way before.

Obviously in this scenario there is no reason why this code couldn't be written in the following way (which I'm very much used to):

var someOtherArray = [];
for (var i = 0; i < array.length; i++) {
    someOtherArray[i] = modifyObjetFromArray(array[i]);
}

But it got me thinking.

Is there a scenario when writing such a for loop would make sense? The idea comes to mind that "array" length may change during the for loop execution, but we don't want to loop further than the original size, but I can't imagine such a scenario.

Shrinking the array inside the loop does not make much sense either, because we are likely to get OutOfBoundsException.

Is there a known design pattern where this annotation is useful?

Edit As noted by @Jerry101 the reason is performance. Here is a link to the performance test I have created: http://jsperf.com/ninforloop. In my opinion difference is not big enough unless you are iterating though a very huge array. The code I copied it from only had up to 20 elements, so I think readability in this case outweighs the performance consideration.

  • In reaction to you edit: this is object oriented programming. Standard array.length is fast and cheap. But for whatever reason, the implementation of .length could change to something expensive. If a value stays the same then don't get it multiple times. – Pieter B Jan 30 '15 at 8:40
  • I agree. It's good to know about this option, but I probably wouldn't use it very often, unless I get a similar example to your sql query. Thank you☺ – Vlad Spreys Jan 30 '15 at 8:43
  • Thing is, if your array is a million items, you're going to call array.length a million times instead of 1 time while the value stays the same. Asking something a million times and knowing that each subsequent answer will be the same as the first answer......just sounds a bit nonsensical to me. – Pieter B Jan 30 '15 at 8:51
  • 1
    I don't find that this makes the code significantly harder to read. (Seriously, if anyone has trouble with such a simple loop, they should be worried.) But: 1) I'd be terribly surprised if after 4 decades of C and C-descended languages, every serious compiler didn't already do this for you; and 2) this may not be a hot spot. It doesn't seem worth making someone spend an extra 5 seconds parsing it unless it's inside a library (e.g. the implementation of a data structure.) – Doval Jan 30 '15 at 12:37
  • 1
    In C#/.NET the variant using n might be slower than the variant using array.Length since the JITter might not notice that it could eliminate array bounds checks. – CodesInChaos Jan 30 '15 at 15:01
27

The variable n ensures the generated code doesn't fetch the array length for every iteration.

It's an optimization that might make a difference in run time depending on the language used, whether the array is actually a collection object or a JavaScript "array", and other optimization details.

  • 3
    No, it doesn't. Whether the array's length is fetched every iteration depends not only on language, but also on compiler and compiler options and what is done in the loop (I am talking about C and C++) – BЈовић Jan 30 '15 at 7:36
  • .. And even so, I would personally put the n assignment just above the loop if I really wanted it, since - as OP noted - this is a rare (YMMV) construct to use and it's easily missed / not understood by other devs reading the code. – cwap Jan 30 '15 at 7:50
  • 20
    As written, it scopes n to the loop, which is typically a good thing. I'd define it before the loop only if I wanted to use it again later. – Jerry101 Jan 30 '15 at 7:53
  • 4
    @Jerry101: Apparently the example snippet is JavaScript, where there is no such thing as a loop scope… – Bergi Jan 30 '15 at 11:29
  • 1
    @BЈовић: in practice, the compiler rarely manages to prove that the array size does not vary (typically it's trivially provable only if the vector over which you are looping is local to the function and you are calling only inlined functions in the loop). – Matteo Italia Jan 30 '15 at 12:28
2

The fetching of the length of the array can easily be "more expensive" then the actual action you iterate over.

So if the set doesn't change, only query the length once.

Not with arrays, but with record-sets coming from an sql server I've seen dramatic improvements by not querying the record-count every iteration. (of-course do this only if you can guarantee you array or record-set doesn't get changed during this process).

  • If the record count changes, then what? Say there were 100 items, and while you are processing item 50, an item after #25 is inserted. So when you process item #51, it's the same as #50 that you already processed, and things go wrong. So the problem is not that length changes, it's much more pervasive. – gnasher729 Apr 17 '17 at 12:10
  • @gnasher729 once you get into asymmetric behavior there's a lot that can go wrong. But there things you can do against that like starting and sql transaction. – Pieter B Apr 17 '17 at 13:30
2

The idea comes to mind that "array" length may change during the for loop execution, but we don't want to loop further than the original size, but I can't imagine such a scenario.

The people talking about performance are probably correct that this is why it was written that way (the person who wrote it that way might not be correct that it significantly affects performance, but that's another matter).

However, to answer this part of your question, I think it's most likely to happen when the loop's main purpose is to modify the array it's looping over. Consider something like this, in pseudo-code:

guests = [];
for (i = 0; i < invitations.length; ++i) {
    if (! invitations[i].is_declined()) {
        guests.append(invitations[i].recipient())
    }
}
// maybe some other stuff to modify the guestlist
for (i = 0, n = guests.length; i < n; ++i) {
    if (guests[i].has_plus_one()) {
        guests.append(guests[i].get_plus_one());
    }
}

Of course there's other ways to write it. In this case I could have checked for plus ones at the same time as checking whether the recipients accepted their invitations, and produced the full guest list one invitation at a time. I don't necessarily want to combine those two operations, but I can't immediately think of a reason why that's definitely wrong and therefore this design is required. It's an option to consider, occasionally.

Actually I think the thing that's most wrong with this code is that membership of the guests array means different things at different points in the code. Therefore I certainly wouldn't call it a "pattern", but I don't know that it's enough of a defect to rule out ever doing anything of this kind :-)

  • Array could also be modified by another thread. This prevents compiler from optimizing out length retrieval. So in question, programmer explicitely says that array does not change even in other threads. – Basilevs Apr 17 '17 at 19:01
0

If at all possible, you should use an iterator that traverses all elements of the array. You use the array just as a sequence, not as random-access storage, so it would be obvious that an iterator that just does a sequential access would be faster. Imagine what you think is an array is actually a binary tree, but someone has written code that determines the "length" by counting the elements, and code that accesses the i-th element, also by traversing the element, each in O (n). An iterator can be very fast, your loop will be sloooooow.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.