如何在Julia中初始化一个结构体数组

6
我想了解在代码中,用什么代替 xxxxx 可以初始化一个包含 1000 个结构体的数组,其中 a 的取值范围是从 20003000(例如,数组的第 1 个元素表示的是 a 的值为 2000,数组的第 2 个元素表示的是 a 的值为 2001,以此类推),而 b 始终为零。
struct MyStruct
    a
    b
end

myArray = Vector{MyStruct}( xxxxx , 1000)

我知道我可以用循环逐个分配值,我只是想知道Julia中是否有更快的方法。


1
请记住,您应该为结构体的字段成员指定类型,否则它将非常缓慢。如果您需要它能够容纳不同类型,请使其参数化。 - DNF
2个回答

8

将其他用户的答案和评论收集在一篇文章中:

不,没有比Julia中的循环更快的方法

与Python和R等其他脚本语言不同,循环在Julia中速度很快。事实上,其他“矢量化”操作(如广播)都是基于Julia循环本身实现的。因此,一个快速的解决方案可能是:

function initialize_vector(range::AbstractRange)
    v = Vector{MyStruct}(undef, length(range))
    @inbounds for i in eachindex(range)
        v[i] = MyStruct(range[i], 0)
    end
    return v
end

广播既快速又方便

广播几乎与循环一样快,有时甚至更加简洁方便。在这种情况下,上面的函数initialize_vector可以写成:

initialize_vector(range::AbstractRange) = MyStruct.(range, 0)

基准测试显示,这两个函数的速度几乎相同。

记得为你的结构体类型指定字段以获得更快的代码

Julia依赖于准确的类型推断来创建快速的、专门的代码。如果MyStruct.aMyStruct.b的类型可以是任何东西,通常无法准确推断应该对MyStruct执行哪种操作。即使在这种情况下,编译器能够推断出类型为Int,每个MyStruct仍然必须包含对堆分配的Int的引用,而不是被分配到栈中。因此,通过简单地更改结构体类型,可以获得10倍的速度提升

struct MyStruct
    a
    b
end

to

struct MyStruct
    a::Int
    b::Int
end

如果您想要Mystruct.a和MyStruct.b的类型能够变化,您可以创建一个带参数的MyStruct,方法如下:
struct MyStruct{T}
    a::T
    b::T
end

如果MyStruct有第三个元素,请将其命名为c,它是一个字符串。如果该字符串是固定长度的,那么这会使代码运行更快吗?在Julia中是否可能实现这一点?如果可以,应该如何实现? - M.E.
@M.E. 很抱歉现在才看到您的评论。是的,如果字符串很短,固定长度的字符串会使您的结构体更快。在Julia中,您无法创建固定长度的字符串。但是,您可以创建自己的固定长度结构体,其行为类似于字符串。可能有一些实现这一功能的包,但我找不到任何一个。如果您的字符串非常短(8个字节或更少),我建议使用UInt64作为后备存储,如果它更大(最多100个字节),我会使用来自StaticArrays包的SArray {Tuple {3},UInt8,1,3} - Jakob Nissen

3
通过点号进行广播在这里非常有效。您还可以为b提供一个向量/集合:
struct MyStruct
    a
    b
end

struct_vec = [MyStruct.(2000:3000, 0)...]
struct_vec2 = [MyStruct.(2000:3000, 0:1000)...]

这将导致一个包含1001个MyStruct元素的数组 Array{MyStruct,1}编辑 如评论所述,... 展开和 [] 不是必需的。此外,为了提高 struct 的性能,您可以指定其字段的类型信息:
struct MyEfficientStruct{T}
    a::T
    b::T
end

struct_vec = MyEfficientStruct.(2000:3000, 0)
struct_vec2 = MyEfficientStruct.(2000:3000, 0:1000)

最后,尽管广播语法比循环更加简洁,Julia的一个巨大优势是循环速度与向量化操作速度一样快(有时甚至更快)。


谢谢,如果我想让变量 a 的值从 200100,步长为 5(即 200、95、90、85、80...),该怎么做? - M.E.
我自己回答一下,以便帮助其他人:vec = [MyStruct.(200:-5:100, 0)] - M.E.
2
在这里,您正在创建一个数组,然后使用 ... 将其展开,然后再将其收集回到一个数组中。这是非常冗余的。只需执行 MyStruct.(2000:3000, 0) 即可。 - DNF
你说得完全正确!我今天稍后会添加一个编辑来指出这一点。 - Wolf
@M.E. 在你的评论中,你正在创建一个单元素向量的向量。你应该删除外部括号。 - DNF
1
@Wolf 我的意思是,完全不要使用括号。没有展开和括号。只需要 MyStruct.(2000:3000, 0) - DNF

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接