Well, in your comprehension the i
is closer to the body of the loop, and in your loops, that’s j
.
the “closest rule” only applies to the shorthand: for i in, j in
. If you have two for ...
, it doesn’t matter if you write them in one line or two lines, it’s the same as:
for i in
for j in
A level? When I’m writing tests I’m frequently nesting like six variables and saving five levels of indentation. And I’m totally unconcerned about the loop order in that context.
A more interesting property than indentation is how break
works.

When I’m writing tests I’m frequently nesting like six variables and saving five levels of indentation.
You could always write
for i for j for k for l for m for n
# one level of indentation
end end end end end end
(I actually do sometimes write nested loops like this because
@threads for i, j
doesn’t work.)

A more interesting property than indentation is how
break
works.
I hadn’t thought about that (I rarely use break
). My initial thought is that the current way makes sense:
julia> for i = 1:2, j = 1:3
println((; i, j))
break
end
(i = 1, j = 1)
vs
julia> for i = 1:2
for j = 1:3
println((; i, j))
break
end
end
(i = 1, j = 1)
(i = 2, j = 1)
In the first case, there’s only one for
, so break
exits the only loop, but in the second case there are two for
s, so break
exits the for
in which it resides.
I think the problem stems from the way Julia stores n-d arrays in memory. The odd execution order of list comprehension like [(i, j) for i = 1:2, j = 1:3]
is IMO mostly an artifact of its memory configuration.
In Julia, an n-d array is stored in such a way that the adjacent elements in the first dimension are consecutive in memory. Consider an array A[i, j]
. This memory configuration means that A[1, 2]
is adjacent to A[2, 2]
, not A[1, 3]
. Therefore, it becomes natural that
julia> [(i, j) for i = 1:2, j = 1:3]
2×3 Matrix{Tuple{Int64, Int64}}:
(1, 1) (1, 2) (1, 3)
(2, 1) (2, 2) (2, 3)
traverses i
first in order to fill consecutive spots in memory. This conflicts with our expectation of loops, where the first iterator (i
in for i in 1:2, j in 1:3
) is expected to be slower changing compared to the ones come after it (j
).
I think the best approach to this is assuming no inner-dependency between the iterators i
and j
when they are used to produce an n-d array. Instead, view them as an single iterator over all elements of such array.
I find @sudete’s argument to be persuasive, and I also tend to avoid for i, j
loops unless I know that I don’t care about the order of iteration, because I have hardwired the [... for i, j]
indexing of array comprehensions into my brain and don’t want to pollute it with a reversed convention.

But it saves a level of indentation
How do you feel about this?
julia> for i in 1:4; for j in 1:i
@show i, j
end; end
(i, j) = (1, 1)
(i, j) = (2, 1)
(i, j) = (2, 2)
(i, j) = (3, 1)
(i, j) = (3, 2)
(i, j) = (3, 3)
(i, j) = (4, 1)
(i, j) = (4, 2)
(i, j) = (4, 3)
(i, j) = (4, 4)
Edit: Oops, bumped an old thread by accident again…