using ShiftedArrays
struct CircularMatrix{T} <: AbstractArray{T,2}
data::Array{T,2}
view::CircShiftedArray
currentIndex::Int
function CircularMatrix{T}(dims...) where T
data = zeros(T, dims...)
CircularMatrix(data, ShiftedArrays.circshift(data, (0, -1)), 1)
end
end
Base.size(M::CircularMatrix) = size(M.data)
Base.eltype(::Type{CircularMatrix{T}}) where {T} = T
function shift_forward!(M::CircularMatrix)
M.shift_forward!(1)
end
function shift_forward!(M::CircularMatrix, n)
# replace the view with a view shifted forwards.
M.currentIndex += n
M.view = ShiftedArrays.circshift(M.data, (n, M.currentIndex))
end
@inline Base.@propagate_inbounds function Base.getindex(M::CircularMatrix, i) = M.view[i]
@inline Base.@propagate_inbounds function Base.setindex!(M::CircularMatrix, data, i) = M.view[i] = data
如何使CircularMatrix像常规矩阵一样起作用。 这样我就可以像
一样访问它m = CircularMatrix{Int}(4,4)
m[1, 1] = 5
x = view(m, 1, :)
答案 0 :(得分:5)
您的矩阵类型被定义为AbstractArray{T, 2}
的子类型。您需要在Julia的非正式数组接口中实现一些用于您的类型的方法,以使在AbstractArray{T, 2}
上起作用的函数和功能也可以在您的自定义类型上起作用,即,使CircularMatrix
可迭代,可索引,功能完全的矩阵。
要实现的方法是
size(M::CircularMatrix)
getindex(M::CircularMatrix, i::Int)
getindex(M::CircularMatrix, I::Vararg{Int, N})
setindex!(M::CircularMatrix, v, i::Int)
setindex!(M::CircularMatrix, v, I::Vararg{Int, N})
您已经实现了1、2和4,但尚未设置索引样式。如果选择linear indexing style,则可能不需要3和5。您只需要将IndexStyle
设置为IndexLinear()
并可能需要进行一些修改,然后一切都应适用于您的矩阵。
size(M::CircularMatrix)
第一个是size
。 size(A::CircularMatrix)
返回尺寸为Tuple
的{{1}}。我相信您的矩阵可能类似于以下内容
A
Base.size(M::CircularMatrix) = size(M.data)
如果选择线性索引样式,则需要此方法。 getindex(M::CircularMatrix, i::Int)
应该为您提供线性索引getindex(M, i::Int)
处的值。您已经在代码中实现了它。如果选择线性索引,则需要为类型设置i
,然后只需跳过3和5。Julia将自动转换多个索引访问,例如IndexStyle
,以访问线性索引。
a[3, 5]
在第二行使用Base.IndexStyle(::Type{<:CircularMatrix}) = IndexLinear()
Base.@propogate_inbounds function Base.getindex(M::CircularMatrix, i::Int)
@boundscheck checkbounds(M, i)
@inbounds M.view[i]
end
可能更好。如果调用者不使用@inbounds
,我们将首先检查边界,并且此希望使后续边界检查不再必要。不过,您可能需要在开发过程中忽略此设置。
@inbounds
第三个用于笛卡尔索引风格。如果选择此样式,则需要实现此方法。签名中的getindex(M::CircularMatrix, I::Vararg{Int, N})
代表“恰好是Vararg{Int, N}
N
个参数”。这里的Int
应该等于N
的维数。由于这是一个矩阵,因此N应该为2。如果选择此样式,则需要定义如下内容
CircularMatrix
或者因为您的尺寸不是参数化的,而矩阵是2D的,那么
Base.@propogate_inbounds function Base.getindex(A::CircularMatrix, I::Vararg{Int, 2})
@boundscheck checkbounds(A, I...)
@inbounds A.view[# convert I[1]` and `I[2]` to a linear index in `view`]
end
Base.@propogate_inbounds function Base.getindex(A::CircularMatrix, i::Int, j::Int)
@boundscheck checkbounds(A, i, j)
@inbounds A.view[# convert i` and `j` to a linear index in `view`]
end
第四个类似于第二个。如果您选择线性索引样式,则此方法应将值设置为线性索引setindex!(M::CircularMatrix, v, i::Int)
。
i
如果选择笛卡尔索引风格,则第五个应该与第三个相似。
在实现1、2和4并设置setindex!(M::CircularMatrix, v, I::Vararg{Int, N})
之后,您应该拥有一个可以正常使用的自定义矩阵类型。
IndexStyle
这些都应该起作用。
有一个有关抽象数组接口here的文档,并带有一些示例。您还可以看到可选方法来实现。
GitHub上有一个JuliaArray组织,提供了许多有用的自定义数组实现,包括m[1, 1] = 5
x = view(m, 1, :)
for e in
...
end
for i in eachindex(m)
...
end
display(m)
println(m)
length(m)
ndims(m)
map(f, A)
....
,StaticArrays
等,还有一个JuliaMatrices组织提供自定义矩阵类型。您可能想看看它们的实现。
OffsetArrays
,则 @inline
是多余的。
@propagate_inbounds
告诉编译器内联一个函数,同时保留调用方的 入站上下文。
Base.@propogate_inbounds
,因为已经有eltype
的定义返回AbstractArray{T, N}
。