`hcat` with generator

I find myself wanting to write code of the kind

hcat(f(i) for i = 1:n)

where f(i) would be a function computing the ith column - or possibly multiple columns - of a matrix.
but I end up getting something weird, 1×1 Array{Base.Generator{UnitRange{Int64},##3#4},2}: Base.Generator{UnitRange{Int64},##3#4}(#3,1:3)

Is such construction feasible? wanted? Is there an alternative simple one-liner that one could use?

1 Like

Maybe you could use collect?

A = reshape(1:12, 3, 4)
f(A,i) = A[:,i]
hcat(collect(f(A,i) for i = 1:4)...)

clunky, but works. Thank you. (My question is still valid though I believe?)

Or just use an array comprehension instead of a generator:

hcat([f(A,i) for i = 1:4]...)
1 Like

That’s in fact how I ended up doing it. Still feels like one step too many.

On 0.6 you could take advantage of dot fusion and do something along the following lines

f(r) = (1:3) .+ 3 .* (r' .- 1)
f(1:4)

On 0.5 you can do the same but won’t be as efficient (you would have to write another function so it doesn’t create unnecessary temporaries, except for the transposition)

g(i, j) =  i + 3 * (j - 1)
f(r) = g.(1:3, r')
f(1:4)
1 Like

By defining the following function:

function genmat(g)
  itr = start(g)
  done(g,itr) && error("generator must provide at least one column block")
  (v,itr) = next(g,itr)
  l = size(v,1)
  vlist = [vec(v)]
  while !done(g,itr)
    (v,itr) = next(g,itr)
    size(v,1) == l || error("all columns must be of same length")
    push!(vlist,vec(v))
  end
  m = vcat(vlist...)
  return reshape(m,l,length(m)÷l)
end

We can simply do:
genmat((f(A,i) for i=1:4))
but also:
genmat((f(A,i:i+1) for i=1:3))
and get an error whith an empty generator, etc. Might be worth the trouble.
Regarding efficiency, it is hard to avoid some allocations without knowing in advance how many and in what shape the column blocks the generator will produce.

I like this, but could one not just rename your genmat to vcat ?(CORR.: I meanthcat`)

vcat is already taken and has a semantic meaning of vertical concatenation. IMHO function names in Julia are very central, and should be very carefully constructed and shared between methods to enable maximum utility when reused. But a name other than genmat is certainly welcome.

hcat((f(A,i) for i = 1:4)...)

works and doesn’t generate an intermediate array.

2 Likes

hcat and genmat both do some allocations. Benchmarking shows genmat does less, and genmat is also 8x faster (on my machine).
It is hard to avoid the allocations and copying without some more information about the generator.

typo - I meant hcat. I am asking why not just change the meaning of hcat(::Generator)

That would be incompatible with what hcat does; it does not concatenate all members of its argument, but rather all its arugments.