在Julia中访问结构数组中字段的最佳方法

时间:2017-10-18 22:38:07

标签: julia

struct Point
   x :: Int
   y :: Int
end

如果我有a类型的数组Array{Point},那么进行字段访问是否有更好的方法(语法更清晰或更快)?

(p->p.x).(a)

2 个答案:

答案 0 :(得分:3)

有四个选项:

1)(p->p.x).(a)

2)[ z.x for z in a ]

3)map(p->p.x, a)

4)getfield.(a, x)(Michael在评论中提出)

前3个将与其他人一样高效,因此归结为个人偏好。方法4在我的机器上有点慢,但正如另一个应答者(Gnimuc)所述,这有望被问题#22710修复。

注意,我经常发现以下方法很有用:

getx(a::Vector{Point}, inds=1:length(a))::Vector{Int} = [ a[i].x for i in inds ]

允许您为任意一组输入索引提取x字段。 (虽然它会比上述3种方法稍微慢一些,以便拉出每个指数)。我的元编程很糟糕,但你实际上可以这样做:

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

它将为您提供上述getx函数,但对于输入类型中的每个字段名...

答案 1 :(得分:3)

最干净的方法是定义您自己的运算符,该运算符最初由 @pabloferz 在话语中发布: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

UPDATE:

目前,如果(p->p.x).(a)很小,快速基准显示a是其他解决方案中最快的。当a的长度变大时,mapcomprehension都会比(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总是比其他方法慢10~20倍,所以最干净的方法并不是高效的。但似乎将来情况会有所改善,我们会为此提供语法糖吗?Make .a syntactic sugar for i->i.a #22710

相关问题