End keyword in creating CartesianIndex

The basic technique Keno was referencing is something that’s generally quite feasible, and I think there is a package somewhere that does it (but I cannot find it at the moment).

julia> struct End end

julia> Base.to_indices(A, inds, I::Tuple{End, Vararg}) = (inds[1][end], to_indices(A, Base.tail(inds), Base.tail(I))...)

With just those two definitions, we can now use End() in many places that didn’t use to be possible:

julia> A = rand(3,5)
3×5 Array{Float64,2}:
 0.0496429  0.1541    0.178673  0.987561    0.100913
 0.273997   0.984579  0.255768  0.862814    0.200435
 0.317385   0.445612  0.106676  0.00631464  0.934804

julia> A[End(), 2]
0.4456115553735922

julia> view(A, End(), 2:3)
2-element view(::Array{Float64,2}, 3, 2:3) with eltype Float64:
 0.4456115553735922
 0.10667647668776192

Now, the trouble you’ll rapidly run into is that you cannot do computation on this End() thing.

julia> A[End() - 1, :]
ERROR: MethodError: no method matching -(::End, ::Int64)

So you can manually add in “deferred” computations, essentially allowing it to build up a thunk that it can evaluate once it knows the size of the array. This is the huge advantage to the end keyword — it is just syntax for the appropriate lastindex function call, which returns a real integer on which you can do real computation.


Now this finally brings us to your actual question, which is can you use this thing with a CartesianIndex? The answer there is a flat no. CartesianIndex is hardcoded to only handle Ints. But the game isn’t over — you can of course construct your own type that will work just like CartesianIndex but allow for more things to be stored in it. It’s just a bit more complicated, but that to_indices is the only API that you need to extend.

2 Likes