If you want to spend your effort on making this code cleaner, the more
Julian thing would be to focus on making it more type-generic, so that it
can handle arguments of more types. For example (requires Julia 0.5):
function mapeBase{T<:Number,S<:Number}(A::AbstractArray{T}, F::AbstractArray{S})
indices(A) != indices(F) && error("arguments must be arrays of the same
shape")
s = zero(float(real(promote_type(T,S))))
count = 0
for i in eachindex(A)
@inbounds if A[i] != 0
s += abs((A[i] - F[i]) / A[i])
count += 1
end
end
return s, count
end
There is no performance penalty to declaring more generic function argument
types. When you pass in Vector{Float64} arrays, a version of mapeBase is
specialized to that type and compiled, just as if you had declared those
types.
Note, by the way, that this way of comparing two arrays is very susceptible
to floating-point roundoff errors — think about what happens if A[i] is
supposed to be 0.0, but is actually 1e-15 due to roundoff. I would
normally recommend something like vecnorm(A - F, 1) / vecnorm(A, 1) instead
-- i.e. take the sum of the |A[i] - F[i]| and divide by the *sum* of |A[i]|.
I agree with other posters that this is a case in which a loop is much
cleaner. It is possible to solve this problem efficiently with reduce in
0.5 (where higher-order functions are now fast), but it would require much
more careful coding and the resulting code would be much more obscure and
unmaintainable, nor would a reduce-based implementation be much shorter as
you have seen.
Higher-order functions are great, and it is great to use them where they
simplify your code. But sometimes a loop is cleaner, and (unlike in other
dynamic languages) there is no reason not to use loops in Julia.