for (i, j) in CartesianIndices(a)
println(i, j)
end
produces an error:
ERROR: iteration is deliberately unsupported for CartesianIndex. Use `I` rather than `I...`, or use `Tuple(I)...`
This is an unreasonable design of the Julia interface in my opinion, which implements unpacking using iterator. I suggest Julia have an additional function unpack
which is called when unpacking syntax is used and only falls back to iterate
by default.
1 Like
Take this:
for (i, j) in Tuple.(CartesianIndices(a))
println(i, j)
end
3 Likes
While the wording of this suggestion is rather forceful, there is merit to it, I think.
1 Like
It was using map
to get around this. Thanks
Note that this (and equivalent map
version) are allocating because they materialize a vector of tuples.
Compare with a non-allocating alternative (unpack2
below) which runs about 7x faster on my machine.
function unpack1(dims)
s = 0
for (i, j) in Tuple.(CartesianIndices(dims))
#println(i, j)
s += i + j
end
return s
end
function unpack2(dims)
s = 0
for ci in CartesianIndices(dims)
i, j = Tuple(ci)
#println(i, j)
s += i + j
end
return s
end
using BenchmarkTools
const dims = (1000, 1000)
@btime unpack1($dims)
@btime unpack2($dims)
Apparently, iteration of a CartesianIndex
is disabled to avoid a possible performance trap when splatting a CartesianIndex
. See #23719
If you want to live dangerously, you could define iteration on CartesianIndex
yourself.
This version (unpack3
below) is also non-allocating, and is also ~7x faster than the “materializing” version.
Base.iterate(ci::CartesianIndex) = iterate(Tuple(ci))
Base.iterate(ci::CartesianIndex, state) = iterate(Tuple(ci), state)
function unpack3(dims)
s = 0
for (i, j) in CartesianIndices(dims)
#println(i, j)
s += i + j
end
return s
end
1 Like
In this case, a generator expression also works well (although in general generators sometimes are way slower due to type-problems), see:
julia> function unpack4(dims)
s = 0
for (i,j) in (Tuple(c) for c in CartesianIndices(dims))
s += i + j
end
return s
end
which on my system is as fast as unpack2
.
1 Like
I thought map
returns a generator, which is how python works. It turns out to return a vector.
Depending on the situation, eager operations is usually more efficient that lazy ones if everything fits into the memory. I’m not sure why in this case it is slower.