Note that, because Function
is an abstract type, accessing the elements of a Vector{Function}
will be type-unstable and cause performance degradation:
julia> d = [t->cos(0t), t->cos(1t), t->cos(2t), t->cos(3t)]
4-element Vector{Function}:
#3 (generic function with 1 method)
#4 (generic function with 1 method)
#5 (generic function with 1 method)
#6 (generic function with 1 method)
julia> d[1](π/4), d[4](π/4)
(1.0, -0.7071067811865475)
julia> using BenchmarkTools
julia> @btime $d[1](0)
20.888 ns (1 allocation: 16 bytes)
1.0
Notice that d
’s element type is Function
, which is an abstract type.:
julia> isabstracttype(Function)
true
this is necessary because each anonymous function inside it is of a different type.
By contrast, a comprehension that generates lambdas will produce a type-stable array:
julia> e = [t->cos(i*t) for i=0:3]
4-element Vector{var"#12#14"{Int64}}:
#12 (generic function with 1 method)
#12 (generic function with 1 method)
#12 (generic function with 1 method)
#12 (generic function with 1 method)
julia> e[1](π/4), e[4](π/4)
(1.0, -0.7071067811865475)
julia> @btime $e[1](0)
4.000 ns (0 allocations: 0 bytes)
1.0
Notice that e
’s element type is var"#12#14"{Int64}
, which is a concrete type:
julia> isabstracttype(var"#12#14"{Int64})
false
Even though all the functions contained in e
are of the same type, they perform the desired task. This is because internally they’re just callable struct
s which capture the value of i
.
julia> Tuple(e[j].i for j=eachindex(e))
(0, 1, 2, 3)