繁体   English   中英

Julia 中具有多个相关参数的参数类型

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM