@nloops with type parameter N

Hey,

I was trying to create a function body which contains @nloops N i A. However, the N depends on the dimension of the array. Following MWE:

A = randn((2,2,2))
function foo(A::AbstractArray{T, N}) where {T, N}
           @nloops N i A begin
                  println("Do something with the indices")
           end
end

foo(A)

However, this throws an error:

ERROR: LoadError: MethodError: no method matching _nloops(::Symbol, ::Symbol, ::Symbol, ::Expr)
Closest candidates are:
  _nloops(::Int64, ::Symbol, ::Symbol, ::Expr...) at cartesian.jl:42
  _nloops(::Int64, ::Symbol, ::Expr, ::Expr...) at cartesian.jl:47
Stacktrace:
 [1] run_repl(::REPL.AbstractREPL, ::Any) at /build/julia/src/julia-1.5.3/usr/share/julia/stdlib/v1.5/REPL/src/REPL.jl:288
in expression starting at REPL[80]:2

I understand this error if the value of N would be only available during runtime. However, N is already determined by type so I would have expected that it could conceptually work.

How can I solve this problem?

Thanks,

Felix

The @nloops macro only accepts literal integers for N. Personally, I think it would make sense to just deprecate the @nloops macro, since there are usually better ways to achieve the same thing. It’s difficult to say what the best solution here would be without a more realistic example, but I would highly recommend reading @tim.holy’s great blog post on multidimensional iteration. CartesianIndices may well be what you are looking for here.

1 Like

You need to use a @generated function foo and then interpolate $N into the returned expression, like the example in the manual.

3 Likes

It seems like that I missed this one completely!
Thanks!

Just for completeness, since I wanted to modify N:

julia> using Base.Cartesian

julia> @generated function foo(A::AbstractArray{T, N}) where {T, N}
           quote 
               @nloops $N i A begin
                   println("Do something with the indices")
               end 
           end 
       end
foo (generic function with 1 method)

julia> @generated function foo2(A::AbstractArray{T, N}) where {T, N}
           N2 = N - 1 
           quote 
               @nloops $N2 i A begin
                   println("Do something with the indices")
               end 
           end 
       end
foo2 (generic function with 1 method)

julia> foo(randn((3,3)))
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices
Do something with the indices

julia> foo2(randn((3,3)))
Do something with the indices
Do something with the indices
Do something with the indices