# 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

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