# For Loop Style Question

Given that `range1`, `range2`, `f()`, and `g()` are set beforehand, what is the preferred way to populate a matrix if we need both indices and values?

``````A=zeros(length(range1),length(range2))
for i in 1:length(range1)-1, j in 1:length(range2)
A[i+1,j]=A[i,j]+f(range1[i])+g(range2[j])
end
``````
``````A=zeros(length(range1),length(range2))
for (i,value1) in enumerate(range1[1:end-1]), (j,value2) in enumerate(range2)
A[i+1,j]=A[i,j]+f(value1)+g(value2)
end
``````

The first seems simpler to me, assuming both of your `range` arrays use 1-based indexing. Note that `range1[1:end-1]` makes a copy (although if this is a `Range` object then it is still efficient because the copy is another `Range`).

Note that I would swap the loop order in order to get better locality (since `A` is stored column-major) for cache-line efficiency.

2 Likes

Isn’t 1-based indexing required by Julia? Thanks for you comments. I wasn’t sure how the loops were parsed when on a single line.

Not at all! Julia `Arrays` have 1 based indexing, but `AbstractArrays` can have any indexing behavior. For a particularly funny example, https://giordano.github.io/blog/2020-05-23-random-array-indexing/

2 Likes

Here you are calculating `f(range1[i])` over and over, once for each iteration over `range2`. You should probably pull this out of the inner loop.

And the first loop example is definitely cleaner.

1 Like

I liked the first way better too. It’s pretty much exactly how I would write the code in Matlab. I wanted to make sure I wasn’t missing some Julian efficiency or style.

Why would I want to use enumerate and create a new variable instead of just calling the original range with an index?

If you want to handle more general objects, e.g. user-defined array types with non 1-based indexing, or even iterable objects that are not indexable at all.

Basically, a little more thought is required to write code that is more generic. But it is perfectly fine to write less-generic, obvious code to start with, and genericize it later as needed (e.g. if you put it into a library that you start re-using in lots of new contexts).

Also, I second @DNF’s suggestion that you may want to precompute `frange1, grange2 = f.(range1), g.(range2)` so that you only evaluate these functions once per range element (if performance matters and these functions are expensive).

1 Like