# \`for i = 1:M, j = 1:N\` discrepancy (loop vs comprehension/generator)

**URL:** https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659
**Category:** Internals & Design
**Tags:** syntax, loops
**Created:** [February 2, 2022, 2:16pm UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659 "2022-02-02T14:16:23Z")
**Posts on this page:** 7
**Page:** 2

<div class="post-metadata">

### Author: ![lrnv](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/lrnv/32/19373_2.png) [@lrnv](https://discourse.julialang.org/u/lrnv)
#### Post date: [February 4, 2022, 3:58pm UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/22 "2022-02-04T15:58:09Z")

</div>

Well, in your comprehension the `i` is closer to the body of the loop, and in your loops, that’s `j`.

---

<div class="post-metadata">

### Author: ![jling](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jling/32/212909_2.png) [@jling](https://discourse.julialang.org/u/jling)
#### Post date: [February 4, 2022, 3:59pm UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/23 "2022-02-04T15:59:42Z")

</div>

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:

```julia
for i in
    for j in

```

---

<div class="post-metadata">

### Author: ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)
#### Post date: [February 4, 2022, 4:47pm UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/24 "2022-02-04T16:47:17Z")

</div>

> [@StevenWhitaker](#):
>
> > [@sijo](#):
> >
> > For me the questionable choice is to allow `for i, j` outside of comprehensions/generators.
> 
> But it saves a level of indentation 😂

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.

---

<div class="post-metadata">

### Author: ![StevenWhitaker](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevenwhitaker/32/9749_2.png) [@StevenWhitaker](https://discourse.julialang.org/u/StevenWhitaker)
#### Post date: [February 4, 2022, 5:21pm UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/25 "2022-02-04T17:21:17Z")

</div>

> [@GunnarFarneback](#):
>
> When I’m writing tests I’m frequently nesting like six variables and saving five levels of indentation.

You could always write

```julia
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.)

> [@GunnarFarneback](#):
>
> 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
julia> for i = 1:2, j = 1:3
           println((; i, j))
           break
       end
(i = 1, j = 1)

```

vs

```julia
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.

---

<div class="post-metadata">

### Author: ![shengjiex98](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/shengjiex98/32/36439_2.png) [@shengjiex98](https://discourse.julialang.org/u/shengjiex98)
#### Post date: [June 22, 2022, 5:08am UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/26 "2022-06-22T05:08:16Z")

</div>

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
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.

---

<div class="post-metadata">

### Author: ![maxkapur](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/maxkapur/32/21208_2.png) [@maxkapur](https://discourse.julialang.org/u/maxkapur)
#### Post date: [June 22, 2022, 7:04am UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/27 "2022-06-22T07:04:06Z")

</div>

I find @sijo’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.

> [@StevenWhitaker](#):
>
> But it saves a level of indentation 😂

How do you feel about this?

```julia
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…

---

<div class="post-metadata">

### Author: ![okatsn](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/okatsn/32/36138_2.png) [@okatsn](https://discourse.julialang.org/u/okatsn)
#### Post date: [December 28, 2022, 2:24am UTC](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/28 "2022-12-28T02:24:34Z")

</div>

In the thread, weights are put on which index should iterates first, with the intrinsic difference between loop and array comprehension being addressed. After reading the entire thread, I find controversies can be reconciled if we focusing on which index should be defined first (if it should be).

Here is my summary, hope it can be helpful. If there are something wrong please point out, thanks!

Following [the reply above](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659), four cases are considered:  
Case 1: `for i = 1:M, j = 1:N ... end` where `j` iterate first  
Case 2: `for i = 1:M for j = 1:N ... end end` where `j` iterate first  
Case 3: `[... for i = 1:M, j = 1:N]` where `i` iterate first  
Case 4: `[... for i = 1:M for j = 1:N]` where `j` iterate first

For Case 3, as clearly indicated in [this reply](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659/14) which acknowledged us that, `for i = 1:M, j = 1:N` in array comprehension the `i` and `j` separated by comma denotes the index of the returned array. As both `[println((; i, j)) for i = j:3, j = 1:3]` and `[println((; i, j)) for i = 1:3, j = i:3]` returned error, obviously `i` and `j` are always independent. In brief, use and think this a syntax for constructing a multi-dimension array where the “order” of iteration follows default behavior for array (column based, which means index i for “row” will iterate first if knowing the order is really an issue).

For Case 2 and 4, the dependency between iterator index is allowed, that we can simply realize the syntax in this case the order of defining variable `i` and `j` (the first defined variable should appear first).  
For example:

```julia
[println((; i, j)) for i = 1:3 for j = i:3]

```

or

```julia
for i=1:3 for j=i:3
         println((; i, j))
    end
end

```

that both return

```julia
(i = 1, j = 1)
(i = 1, j = 2)
(i = 1, j = 3)
(i = 2, j = 2)
(i = 2, j = 3)
(i = 3, j = 3)

```

In final for Case 1, I realize it as a syntax sugar that this nested loop `for i = 1:M, j = 1:N` tells us `i` is defined first, with `j` defined subsequentially to be iterate, for example:

```julia
julia> for i=1:3, j=i:3
       println((; i, j))
       end
(i = 1, j = 1)
(i = 1, j = 2)
(i = 1, j = 3)
(i = 2, j = 2)
(i = 2, j = 3)
(i = 3, j = 3)

```

where in reverse (`for i=j:3, j=1:3 ...`) also returns `ERROR: UndefVarError: j not defined`.

To sum up, controversies arise only when we regard array comprehension `[... for i = 1:M, j = 1:N]` as a “nested for loop” which allows tasks to be dependent one by one; but it is not.

**In all variant syntaxes for “nested for loop”** , the index to be defined first have to appear first **as dependencies between iterations or iterations of different iterators are allowed** , and nested loops are executed in the order that just **allows** the cases that one have to wait the previous result. Thus, the order of executing nested for loop becomes intuitive considering the scope should not be refreshed (e.g., earlier defined `i` jumping to the next value) until all dependent variables (later defined `j`, `k` and etc. ) are all consumed.

**In the case of array comprehension using comma separates indices,** e.g., `[... for i = 1:M, j = 1:N]`, the syntax shouldn’t be realized as “nested for loop” since **any interdependency between iterations of different iterators is not allowed**. Thus, the “order” of executing the body of array comprehension just depends on the default behavior of constructing an array in julia.

[Previous page](https://discourse.julialang.org/t/for-i-1-m-j-1-n-discrepancy-loop-vs-comprehension-generator/75659.md?page=1)
