朱莉娅语言:不同类型的数字数组初始化

4
我试图在Julia中构建一个包含两个元素的数组,其中每个子数组都有不同的类型(一个是Int64向量,另一个是Float32数组)。
下面的代码会自动将我想要的Int64元素转换为Float32,这正是我不想要的:
my_multitype_array = [ collect(1:5), rand(Float32,3) ]

生成的数组自动将第一个数组中的Int64转换为Float32,并且生成的my_multitype_array 的类型为 2-element Array{Array{Float32,1}}。如何强制使第一个子数组保持为Int64?我需要在填充值之前预定义my_multitype_array为两个所需类型的空数组吗?
最后,一旦我有了所需具有不同类型的数组,当在函数中预先声明其类型时,我该如何引用它呢?参见下面的示例:
function foo_function(first_scalar_arg::Float32, multiple_array_arg::Array{Array{Float32,1}})
       # do stuff
       return
end

我应该写::Array{Array{Any,1}}还是其他什么,来替代::Array{Array{Float32,1}}


1
在你所得到的答案中,你可以使用Union类型。但你也可以考虑一下外部结构是元组而不是数组。这可能会更有效率,因为它在编译时就已经知道了两个内部类型。 - Antonello
3个回答

4

我认为以下代码更符合问题的要求:

julia> a = Union{Array{Int},Array{Float64}}[[1,2,3],rand(2,2)]
2-element Array{Union{Array{Float64,N} where N, Array{Int64,N} where N},1}:
 [1, 2, 3]
 [0.834902264215698 0.42258382777543124; 0.5856562680004389 0.6654033155981287]

这将创建一个实际的数据结构,它知道它包含 Float64Int 数组。

一些用法

julia> a[1]
3-element Array{Int64,1}:
 1
 2
 3

julia> a[2]
2×2 Array{Float64,2}:
 0.834902  0.422584
 0.585656  0.665403

并操作结构:

julia> push!(a, [1, 1]); #works

julia> push!(a, [true, false]);
ERROR: MethodError: no method matching Union{Array{Float64,N} where N, Array{Int64,N} where N}(::Array{Bool,1})

好的,谢谢。关于问题的第二部分(在函数中注释类型),那么我是否需要预先声明参数的类型为 arg::Array{Union{Array{Float64,N} where N, Array{Int64,N} where N},1} - Conor
1
除非您需要在函数体中使用N的信息,否则不需要编写where N。因此,通常只需编写arg :: Vector {Union {Array {Float64},Array {Int64}}}即可。但是,如果您需要类型中的数组大小信息,则会变得更加复杂。例如,如果维数取决于元素类型,则可以使用function f(arg :: Vector {Union {Array {Float64,N1},Array {Int64,N2}}})where {N1,N2} - Przemyslaw Szufel

3

如何实例化不同类型的向量:

如果在终端输入向量,它将被提升为最大的通用类型:

julia> [[1], [1.0]]
2-element Array{Array{Float64,1},1}:
 [1.0]
 [1.0]

这是由于您没有指定外部向量的类型,所以Julia将基于内容来推断其类型。更具体的类型总是更有效率的,因此,如果可以将向量类型转换为可以表示所有内部向量的单一类型,则会通过“promote”机制实现此目的。为避免这种情况,您需要手动指定外部向量的类型,例如:
julia> Any[[1], [1.0]]
2-element Array{Any,1}:
 [1]
 [1.0]

如何引用不同类型的向量

仔细想一想,“不同类型的向量”并不是一个单一的类型,而是一个无限集合的类型。这种类型被称为Julia中的“联合类型”,并由where关键字表示。在这种情况下,您需要使用Vector{T} where T <: Vector

但等等!为什么:

julia> Any[[1], [1.0]] isa Vector{T} where T <: Vector
false

一个可以包含任何元素的向量并不是真正的向量的向量。所以您有两个选择:

  • 要么通过删除类型注释或显著放宽它们来缓解函数签名(这是首选,因为您传递的值实际上可能是一个向量的向量,即使其类型是例如Vector{Any}):
function foo_function(first_scalar_arg, multiple_array_arg::AbstractArray)
       # do stuff
       return
end

否则请注意确保最初构建“向量的向量”:
julia> Vector[[1], [1.0]]
2-element Array{Array{T,1} where T,1}:
 [1]
 [1.0]

julia> Vector[[1], [1.0]] isa Vector{T} where T <: Vector
true

谢谢你的回答 - 我接受了另一个答案,因为它更直接和适合我最初提出的问题,但是你的回答实际上对我来说更加适合/有教育意义,因为它解释了与这个问题相关的各种概念,所以非常感谢 :) - Conor

2
稍微解释一下 @Przemyslaw Szufel 的答案...
创建混合类型元素的向量有些棘手,正如你所见,因为字面数组构造函数试图将元素提升为公共类型。有一个特殊的语法来解决这个问题,在这里中描述。
在你的情况下,你可以按照以下方式构建你的向量向量:
julia> Union{Vector{Int64}, Vector{Float32}}[[1, 2], [1.0f0, 2.0f0]]
2-element Array{Union{Array{Float32,1}, Array{Int64,1}},1}:
 [1, 2]
 Float32[1.0, 2.0]

文字翻译:字面量数组构造函数的前缀指定了数组的元素类型。因此,在这种情况下,向量的元素类型受到限制。
意思是:在使用字面量数组构造函数时,前缀可以指定数组的元素类型。因此,在这个例子中,向量的元素类型被限定为特定的类型。
Union{Vector{Int64}, Vector{Float32}}

换句话说,外向量的元素必须是Int64向量或Float32向量。

1
你写的方式比我的更好,所以我更新了我的答案。当然,(T where T<:Union{Array{Int},Array{Float64}}) === Union{Array{Int},Array{Float64}}true,因此我过度使用了括号。 - Przemyslaw Szufel

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