# Apply function to array in Julia

Dear all,
I am reading a book on how to create a procedure for solving integrants using python. It is said to create an array and apply the function over an array in order to speed up the procedure. The code provided is:

``````from numpy import linspace, sum

def midpoint(f, a, b, n):
h = float(b-a)/n
x = linspace(a+h/2, b-h/2, n)
return h*sum(f(x))
``````

where f is the function to be integrated between the pioints a and b, and n is the number of iteractions.
I converted this in Julia with:

``````function midpoint(f, a, b, n)
h = (b-a)/n
res = 0
x = collect(range(z+h/2,stop=b-h/2,length=n) )
res = sum(f(x))
return res
end
``````

x has been correctly generated: I set a=0, b=1, n=100 and:

``````julia> x
100-element Array{Float64,1}:
0.005
0.015
0.025
0.035
⋮
0.965
0.975
0.985
0.995
``````

and I assigned a function to f:

``````julia> f=x->x^3+x^2
#11 (generic function with 1 method)
``````

But when I try the apply it:

``````julia> res = sum(f(x))
ERROR: MethodError: no method matching ^(::Array{Float64,1}, ::Int64)
Closest candidates are:
^(::Float16, ::Integer) at math.jl:795
^(::Missing, ::Integer) at missing.jl:120
^(::Missing, ::Number) at missing.jl:93
...
Stacktrace:
 macro expansion at ./none:0 [inlined]
 literal_pow at ./none:0 [inlined]
 (::getfield(Main, Symbol("##13#14")))(::Array{Float64,1}) at ./none:1
 top-level scope at none:0
``````

What am I missing? how can I apply a function to all elements of an array directly?

Thank you

try `sum(f,x)` or `sum(f.(x))`

3 Likes

Thank you!

You want to broadcast `f` over the array `x`, so you’re looking for `f.(x)`. Better still, you can avoid allocations by passing `f` directly to sum, i.e. `sum(f, x)`. You might also want to take a look at https://docs.julialang.org/en/v1/manual/arrays/index.html#Array-and-Vectorized-Operators-and-Functions-1. Also note that the `collect` in the function above is unnecessary in most cases and you will get better performance by not collecting `x` into an array.

but if I don’t use collect I don’t get an array:

``````julia> range(Start+h/2,stop=End-h/2,length=Iter)
0.005:0.01:0.995
julia> collect(range(Start+h/2,stop=End-h/2,length=Iter))
100-element Array{Float64,1}:
0.005
0.015
0.025
0.035
⋮
0.965
0.975
0.985
0.995
``````

Incidentally, there is no reason to pre-assign `res = 0`, or even to name it as a variable. Just `sum(f, x)` in the last line would be fine.

Why do you want an array specifically? The range contains the same information, only more efficiently.

2 Likes

A range is an array, it just isn’t an `Array`. Specifically, it’s a `StepRangeLen` which is a type of `AbstractArray`.

You should definitely not use `collect` here, in fact, you should almost never collect ranges. The whole point of ranges is that they are smarter and faster and lighter than a full `Array`. Using `collect` just ruins that.

You should write

``````x = range(z+h/2, b-h/2, length=n)
res = sum(f, x)
``````

Not `sum(f.(x))`. This is the best and fastest way.

2 Likes

I will be happy NOT to use collect, but so why the difference in output that I have shown?

0.005:0.01:0.995

vs

100-element Array{Float64,1}:

In fact, following the manual:

``````julia> x = range(1, stop=100, step=10)
1:10:91

julia> println(x)
1:10:91

julia> x = collect(range(1, stop=100, step=10))
10-element Array{Int64,1}:
1
11
21
31
41
51
61
71
81
91

julia> println(x)
[1, 11, 21, 31, 41, 51, 61, 71, 81, 91]
``````

If you really want it in array form, you can use the range object to build an array:

`julia> collect(range(1, length=12, stop=100))`

I don’t need an `Array`, and I can see that range is an array:

``````julia> x = range(1, stop=100, step=10)
1:10:91

julia> typeof(x)
StepRange{Int64,Int64}
``````

but I do I use `range` then? for iteractions, for instance:

``````julia> for i in x

println(x)

end
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
1:10:91
``````

Well, of course they print differently, otherwise how could you tell that one is a smart compact array, and the other a dumb big one? Presumably, you mean

``````for i in x
println(i)
end
``````

?

BTW, for integer ranges it is more convenient to write `1:10:100` rather than `range(1, stop=100, step=10)`.

2 Likes

The `StepRange` prints differently than an `Array`, because it is a different kind of object, but it does everything almost everything an `Array` can do, for instance indexing,

``````julia> x
21

julia> collect(x)
21
``````

scalar multiplication,

``````julia> 3.14159 * x
3.14159:31.4159:285.88469 # note the result is another StepRange
``````

and matrix-array multiplication:

``````julia> M = randn(10, 10);
julia> M * x # "just works"
``````

Edit But not mutation:

``````julia> x = 22
ERROR: setindex! not defined for StepRange{Int64,Int64}
``````

because `StepRanges` are immutable, and, more fundamentally, because the value at `x` isn’t actually stored anywhere: it’s calculated on-demand when you ask for it.

1 Like

Ops! I see what you mean.

Except being mutated

1 Like