How to CartesianIndex array comprehension?

Hey community :smiley:

julia> CartesianIndex(1,1) .+ [CartesianIndex(i,i) for i in 1:10]      
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] iterate(::CartesianIndex{2}) at .\multidimensional.jl:164
 [3] copyto!(::Array{Int64,1}, ::CartesianIndex{2}) at .\abstractarray.jl:721
 [4] _collect(::UnitRange{Int64}, ::CartesianIndex{2}, ::Base.HasEltype, ::Base.HasLength) at .\array.jl:609
 [5] collect(::CartesianIndex{2}) at .\array.jl:603
 [6] broadcastable(::CartesianIndex{2}) at .\broadcast.jl:665
 [7] broadcasted(::Function, ::CartesianIndex{2}, ::Array{CartesianIndex{2},1}) at .\broadcast.jl:1235
 [8] top-level scope at REPL[5]:1

(CartesianIndex(1,1),) .+ [CartesianIndex(i,i) for i in 1:10]

Wrapping it in a tuple indicates the first argument is to be broadcast. This is arguably a case where defensive programming gets in the way. If we were confident that no one would try to broadcast over the “elements” of a CartesianIndex we could make what you tried work.

3 Likes

Why not replace it with:

[CartesianIndex(1,1) + CartesianIndex(i,i) for i in 1:10]

E.g.,

julia> v = reshape(1:121,11,11)
11×11 reshape(::UnitRange{Int64}, 11, 11) with eltype Int64:
  1  12  23  34  45  56  67  78  89  100  111
  2  13  24  35  46  57  68  79  90  101  112
  3  14  25  36  47  58  69  80  91  102  113
  4  15  26  37  48  59  70  81  92  103  114
  5  16  27  38  49  60  71  82  93  104  115
  6  17  28  39  50  61  72  83  94  105  116
  7  18  29  40  51  62  73  84  95  106  117
  8  19  30  41  52  63  74  85  96  107  118
  9  20  31  42  53  64  75  86  97  108  119
 10  21  32  43  54  65  76  87  98  109  120
 11  22  33  44  55  66  77  88  99  110  121

julia> v[[CartesianIndex(1,1)+CartesianIndex(i,i) for i in 1:10]]
10-element Array{Int64,1}:
  13
  25
  37
  49
  61
  73
  85
  97
 109
 121

…if that is what you want.

Oh I understand. It’s not clear what to do…

julia> [each for each in CartesianIndex(1,1)]
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] iterate(::CartesianIndex{2}) at .\multidimensional.jl:164
 [3] iterate at .\generator.jl:44 [inlined]
 [4] collect(::Base.Generator{CartesianIndex{2},typeof(identity)}) at .\array.jl:665
 [5] top-level scope at REPL[13]:1

julia> iterate(CartesianIndex(1,1))
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] iterate(::CartesianIndex{2}) at .\multidimensional.jl:164
 [3] top-level scope at REPL[14]:1

julia> for each in CartesianIndex(1,1) println(each) end
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
Stacktrace:
 [1] error(::String) at .\error.jl:33
 [2] iterate(::CartesianIndex{2}) at .\multidimensional.jl:164
 [3] top-level scope at .\REPL[15]:1

Right. The bottom line is we want people to use CartesianIndex like a scalar; A[3, 4] should be identical in every respect with A[CartesianIndex(3, 4)]. That imposes certain rules with respect to broadcasting, including the fact that you can’t iterate over (3, 4) as if it’s a list. But what we don’t (yet) do is trust that people won’t try to iterate over those entries, so we look for that and try to throw a helpful error. However, in your case, that attempt to be helpful actually got in the way of the perfectly well-justified idea you had in the beginning.

4 Likes