struct Point
x :: Int
y :: Int
end
If I have an array a
of type Array{Point}
, is there a better way (either syntactically cleaner or faster) to do field access than this?
(p->p.x).(a)
Four options are:
1) (p->p.x).(a)
2) [ z.x for z in a ]
3) map(p->p.x, a)
4) getfield.(a, x)
(suggested by Michael in the comments)
The first 3 will be about as efficient as each other, so it comes down to personal preference. Method 4 is a bit slower on my machine, but as the other answerer (Gnimuc) states, this will hopefully be fixed up by issue #22710.
Note, I often also find the following method useful:
getx(a::Vector{Point}, inds=1:length(a))::Vector{Int} = [ a[i].x for i in inds ]
which allows you to pull out the x
field for an arbitrary set of input indices. (although it will be slightly slower than the above 3 methods for pulling out every index). My metaprogramming sucks, but you can actually do something like this:
for fn in fieldnames(Point)
eval(parse("get$(fn)(a::Vector{Point}, inds=1:length(a))::Vector{Int} = [ a[i].$(fn) for i in inds ]"))
end
which will get you the above getx
function but for every fieldname in the input type...
The cleanest way is to define your own operator which was originally posted by @pabloferz on Discourse: https://discourse.julialang.org/t/broadcast-over-getfield-in-0-6/2335/4
struct Point
x :: Int
y :: Int
end
a = [Point(i,j) for i = 1:10 for j = 1:10]
↦(val, s) = getfield(val, s)
a .↦ :x
a .↦ :y
For now, a quick benchmark shows (p->p.x).(a)
is the fastest among other solutions if a
is small. when the length of a
grows large, both map
and comprehension
are slightly faster than (p->p.x).(a)
:
julia> versioninfo()
Julia Version 0.6.0
Commit 903644385b* (2017-06-19 13:05 UTC)
......
julia> @btime (p->p.x).($a)
88.283 ns (1 allocation: 896 bytes)
julia> @btime [ z.x for z in $a ]
109.578 ns (2 allocations: 912 bytes)
julia> @btime map(p->p.x, $a)
163.485 ns (3 allocations: 944 bytes)
julia> @btime getfield.($a,:x)
1.586 μs (101 allocations: 4.00 KiB)
julia> a = [Point(i,j) for i = 1:100 for j = 1:100]
julia> @btime getfield.($a,:x);
160.845 μs (10002 allocations: 390.70 KiB)
julia> @btime (p->p.x).($a);
9.817 μs (2 allocations: 78.20 KiB)
julia> @btime map(p->p.x, $a);
8.306 μs (3 allocations: 78.22 KiB)
julia> @btime [ z.x for z in $a ];
8.306 μs (3 allocations: 78.22 KiB)
getfield
is always 10~20x slower than other methods, so the cleanest way is not performant. But it seems that the situation is going to be improved in the future, we'll have a syntax sugar for this?: Make .a syntactic sugar for i->i.a #22710
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With