我有一个实现Array接口的结构。我想在访问它时重新定义索引。到目前为止,我已经在我的类型的Base.getindex
函数中做到了,但是我已经在文档中看到了Base.to_indices
函数,并且不知道它们如何协同工作。
可以使用:
(Colon
),UnitRange
,StepRange
,OneTo
,Int
或{{ 1}},那么我应该在哪里重新定义索引而不必处理所有这些情况?
答案 0 :(得分:2)
很难抽象地讨论这一点。这是一个具体的例子:
struct ReversedRowMajor{T,A} <: AbstractMatrix{T}
data::A
end
ReversedRowMajor(data::AbstractMatrix{T}) where {T} = ReversedRowMajor{T, typeof(data)}(data)
Base.size(R::ReversedRowMajor) = reverse(size(R.data))
Base.getindex(R::ReversedRowMajor, i::Int, j::Int) = R.data[end-j+1, end-i+1]
这个简单的数组访问父数组,并对其索引进行置换(将其变为行主)并进行转换(将其反转)。请注意,此数组会自动支持所有预期的索引形式:
julia> R = ReversedRowMajor([1 2; 3 4; 5 6])
2×3 ReversedRowMajor{Int64,Array{Int64,2}}:
6 4 2
5 3 1
julia> R[:, isodd.(R[1,:].÷2)]
2×2 Array{Int64,2}:
6 2
5 1
julia> @view R[[1,4,5]]
3-element view(reshape(::ReversedRowMajor{Int64,Array{Int64,2}}, 6), [1, 4, 5]) with eltype Int64:
6
3
2
请注意,我们不会使用排列后的索引和计算后的索引重新索引到R
中—新索引直接提供给父数组R.data
。
现在,to_indices
另一方面将以前不受支持的索引 types 转换为Int
或Int
的数组,然后重新使用这些转换后的索引将索引自身R
中索引。请注意当您致电R[Int8(1),Int8(1)]
时会发生什么:
julia> @which R[Int8(1),Int8(1)]
getindex(A::AbstractArray, I...) in Base at abstractarray.jl:925
这没有调用您定义的任何方法-尚未。您尚未定义如何getindex(::ReversedRowMajor, ::Int8, ::Int8)
。因此,朱莉娅正在为您处理此案。它使用to_indices
将Int8
转换为Int
,然后再次调用R[1,1]
。现在,它会命中您定义的方法。
简而言之:该数组具有一个简单的getindex
方法,该方法带有Int
索引,可将访问重新计算为父数组。另一方面,在您尚未定义匹配方法的情况下,to_indices
会将所有其他类型的索引转换为支持的索引到同一数组。您根本无法使用to_indices
进行所需的转换,因为不清楚R[1, 2]
是使用转换前的索引还是转换后的索引。