[英]Parametric types with multiple dependent parameters in Julia
我试图用多个参数来理解 Julia 中的参数类型。 这是一个简单的例子。 假设我想为二进制向量定义一个类型,其中向量在内部表示为 integer 的二进制扩展中的位。 例如,向量 (1,0,1,1) 将由 integer 13 表示。
在 Julia 中实现此目的的一种方法是定义具有两个参数的参数类型BinaryVector{n,T}
: n
是向量的维度, T
是向量的内部表示的类型,例如UInt8
。
abstract type AbstractBinaryVector end
struct BinaryVector{n, T} <: AbstractBinaryVector
a::T
function BinaryVector{n, T}(a::T) where {n, T<:Integer}
return new(a)
end
end
为方便起见,我想定义一个外部构造方法,它只需要指定参数n
并使用基于n
的合理默认值T
。 我可以默认使用无符号 integer 类型,其具有足够的位来指定长度为n
的二进制向量:
function typerequired(n::Integer)
if n ≤ 128
bitsrequired = max(8, convert(Integer, 2^ceil(log2(n))))
return eval(Meta.parse("UInt"*string(bitsrequired)))
else
return BigInt
end
end
function BinaryVector{n}(a::Integer) where {n}
T = typerequired(n)
return SymplecticVector{n, T}(a)
end
对于任何 integer 类型T
,这是否隐式定义了一个新的参数类型BinaryVector{n}
,其中BinaryVector{n,T}
是BinaryVector{n}
的子类型? 请注意,我实际上不需要类型BinaryVector{n}
,我只想要一种为参数T
设置默认值的便捷方法,因为例如当n
为 4 时, T
几乎总是UInt8
。
当我定义用于生成随机二进制向量的函数时, BinaryVector{n}
和BinaryVector{n,T}
之间的这种区别以一种意想不到的方式表现出来。 这是我的做法。 下面的第一个 function 使用例如rand(BinaryVector{4,UInt8})
,它返回和 object 类型为BinaryVector{4,UInt8}
。 除了生成随机二进制向量的 arrays 之外,第二个 function 是相同的。 第三个 function 称为rand(BinaryVector{4})
并假定参数T
的默认值。 第四个是第三个function的阵列版本。
import Base: rand
import Random: AbstractRNG, SamplerType
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n, T}}) where {n, T}
return BinaryVector{n, T}(rand(rng, 0:big(2)^n-1)...)
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n, T}}, dims...) where {n, T}
return BinaryVector{n, T}.(rand(rng, 0:big(2)^n-1, dims...))
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n}}) where {n}
T = typerequired(n)
return rand(BinaryVector{n, T})
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n}}, dims...) where {n}
T = typerequired(n)
return rand(BinaryVector{n, T}, dims...)
end
前三个函数按预期工作:
julia> a = rand(BinaryVector{4, UInt8})
BinaryVector{4, UInt8}(0x06)
julia> typeof(a)
BinaryVector{4, UInt8}
julia> b = rand(BinaryVector{4, UInt8}, 3)
3-element Vector{BinaryVector{4, UInt8}}:
BinaryVector{4, UInt8}(0x05)
BinaryVector{4, UInt8}(0x00)
BinaryVector{4, UInt8}(0x0e)
julia> typeof(b)
Vector{BinaryVector{4, UInt8}} (alias for Array{BinaryVector{4, UInt8}, 1})
julia> c = rand(BinaryVector{4})
BinaryVector{4, UInt8}(0x05)
julia> typeof(c)
BinaryVector{4, UInt8}
但是在使用最后一个 function 时:
julia> d = rand(BinaryVector{4}, 3)
3-element Vector{BinaryVector{4}}:
BinaryVector{4, UInt8}(0x07)
BinaryVector{4, UInt8}(0x0e)
BinaryVector{4, UInt8}(0x0b)
julia> typeof(d)
Vector{BinaryVector{4}} (alias for Array{BinaryVector{4}, 1})
d
的元素类型为BinaryVector{4}
而不是BinaryVector{4,UInt8}
。 有没有办法强制此 function 返回Vector{BinaryVector{4,UInt8}}
类型的 object 而不是Vector{BinaryVector{4}}
类型的东西?
或者,有没有更好的方法来做这一切? 我不只是首先定义类型BinaryVector{n}
并且始终使用默认的无符号 integer 类型作为内部表示的原因是,如果我每次创建二进制向量时调用所需的typerequired
会很昂贵'正在创建大量的二进制向量。
完整代码示例:
abstract type AbstractBinaryVector end
struct BinaryVector{n, T} <: AbstractBinaryVector
a::T
function BinaryVector{n, T}(a::T) where {n, T<:Integer}
return new(a)
end
end
function typerequired(n::Integer)
if n ≤ 128
bitsrequired = max(8, convert(Integer, 2^ceil(log2(n))))
return eval(Meta.parse("UInt"*string(bitsrequired)))
else
return BigInt
end
end
function BinaryVector{n}(a::Integer) where {n}
T = typerequired(n)
return SymplecticVector{n, T}(a)
end
import Base: rand
import Random: AbstractRNG, SamplerType
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n, T}}) where {n, T}
return BinaryVector{n, T}(T(rand(rng, 0:big(2)^n-1)))
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n, T}}, dims...) where {n, T}
return BinaryVector{n, T}.(T.(rand(rng, 0:big(2)^n-1, dims...)))
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n}}) where {n}
T = typerequired(n)
return rand(BinaryVector{n, T})
end
function rand(rng::AbstractRNG, ::SamplerType{BinaryVector{n}}, dims...) where {n}
T = typerequired(n)
return rand(BinaryVector{n, T}, dims...)
end
a = rand(BinaryVector{4, UInt8})
b = rand(BinaryVector{4, UInt8}, 3)
c = rand(BinaryVector{4})
d = rand(BinaryVector{4}, 3)
我相信 rand() 正在简化您的类型规范。 通常这不会影响最终的 output。 但是,您可以通过使用 for 循环构造来避免使用 rand() 的向量构造版本:
julia> d2 = [rand(BinaryVector{4}) for _ in 1:3]
3-element Vector{BinaryVector{4, UInt8}}:
BinaryVector{4, UInt8}(0x09)
BinaryVector{4, UInt8}(0x0a)
BinaryVector{4, UInt8}(0x0a)
julia> typeof(d2)
Vector{BinaryVector{4, UInt8}} (alias for Array{BinaryVector{4, UInt8}, 1})
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.