# Generic array scan line

I have:

``````const w, h = 640, 480
a = rand(Float32, w, h)
``````

and an algorithm, which requires scanning array `a` by row or column in all possible directions like shown here:

``````function scan4(a)
scanLine = zeros(max(w, h))

for i in 1:h
foo!(scanLine, a[:, i])
end

scanLine = zeros(max(w, h))

for i in h:-1:1
foo!(scanLine, a[:, i])
end

scanLine = zeros(max(w, h))

for i in 1:w
foo!(scanLine, a[i, :])
end

scanLine = zeros(max(w, h))

for i in w:-1:1
foo!(scanLine, a[i, :])
end
end
``````

Is there a magic indexing trick, which would allow rewriting `scan4` function avoiding repetitions to get something like this without dramatic loss of performance:

``````function scan42(a)
for indexingScheme in [colLeftRight, colRightLeft, rowTopDown, rowBottomUp]
scanLine = zeros(max(w, h))

for i in magicIndex(a, indexingScheme)
foo!(scanLine, magicSlice(a, i, indexingScheme))
end
end
end
``````

I’m looking for an existing API, which does something similar, not how to write something new from scratch using macros, unless it’s fairly simple and idiomatic to Julia.

You can write generators like

``col_left_right = ( view(a, :, i) for i in 1:size(a, 2) )``
1 Like

Isn’t it `eachcol`?

It is for `colLeftRight` scan, but can I get `eachcol` in reverse for `colRightLeft` scan?

It looks like you can do `Iterators.reverse(eachcol(xs))` (but not `reverse(eachcol(xs))`).

3 Likes

This is another option, but I don’t know if it’s efficient:

``````eachcol(reverse(a, dims=2))
``````

`reverse` copies an array. It’s not efficient if you have a gigantic array.

3 Likes

Should be `Iterators.reverse.(eachcol(xs))` from my understanding.

In that case, `(Iterators.reverse(c) for c in eachcol(xs))` for a non-allocating version. `Iterators.map(Iterators.reverse, eachcol(xs))` would also work in Julia 1.6.

1 Like

Read this part only after writing the below. My bad Technically, you can do

``````function scan4(a)
w, h = size(a)
magic_iterator = (eachcol,
xs -> (@view(c[end:-1:1]) for c in eachcol(xs)),
eachrow,
xs -> (@view(c[end:-1:1]) for c in eachrow(xs)))

for f in magic_iterator
scanLine = zeros(max(w, h))
for x in f(a)
foo!(scanLine, x)
end
end
end
``````

but this is not type stable (the type of `f` changes each iteration) so not that good of an idea. You can generate the 4 loops with:

``````@generated function scan4_gen(a)
magic_iterator = (eachcol,
xs -> (@view(c[end:-1:1]) for c in eachcol(xs)),
eachrow,
xs -> (@view(c[end:-1:1]) for c in eachrow(xs)))

expr = quote w, h = size(a) end

for i in 1:length(magic_iterator)
iter = quote
scanLine = zeros(max(w, h))
for x in \$(magic_iterator[i])(a)
foo!(scanLine, x)
end
end
push!(expr.args, iter)
end

expr
end
``````

which “saves typing” and is fast, but at great expense to readability… If you wanted to generalize to scanning N-D arrays, a generated function is what I would suggest. For this though ymmv. I can’t think of another way that doesn’t also incur its own costs.

I went with `@view(c[end:-1:1]` since `Iterators.reverse` doesn’t support iteration (maybe not necessary though). Note that in 1.5+, you should use `@view(c[end:-1:begin])`.

1 Like