Alec has marked this topic as resolved.
I use this pattern so frequently that I'd like to put it in a package for easy use. The macro itself and also reexport the components of MicroCollections.jl and BangBang.jl.
MapUnroll
?Mason Protter said:
Of course it's a bit awful to have to write the loop body twice. Sometimes I wish we had a macro
@pushalloc for i in 1:n
which would expand to something like this -- separate the first iteration, find everypush!
and allocate space using the first time's types. But I never wrote one.What I use for stuff like this is an
@unroll
macro:macro unroll(N::Int, loop) Base.isexpr(loop, :for) || error("only works on for loops") Base.isexpr(loop.args[1], :(=)) || error("This loop pattern isn't supported") val, itr = esc.(loop.args[1].args) body = esc(loop.args[2]) @gensym loopend label = :(@label $loopend) goto = :(@goto $loopend) out = Expr(:block, :(itr = $itr), :(next = iterate(itr))) unrolled = map(1:N) do _ quote isnothing(next) && @goto loopend $val, state = next $body next = iterate(itr, state) end end append!(out.args, unrolled) remainder = quote while !isnothing(next) $val, state = next $body next = iterate(itr, state) end @label loopend end push!(out.args, remainder) out end
That's a good idea! I don't particularly want to own it, and I'd have no problem with you publishing and owning it. However, if you want we can put it in the JuliaFolds2 organization, and make you a co-owner.
Last updated: Jul 22 2025 at 04:56 UTC