Array range complement

后端 未结 2 918
执笔经年
执笔经年 2021-01-18 05:41

Is there a way to overwrite [] to have complement of range in array?

julia> a=[1:8...]
8-element Array{Int64,1}:
 1
 2
 3
 4
 5
 6
 7
 8

jul         


        
相关标签:
2条回答
  • 2021-01-18 06:06

    I haven't looked into the internals of indexing before, but at a first glance, the following might work without breaking too much:

    immutable Not{T}
        idx::T
    end    
    
    if :to_indices in names(Base)
        # 0.6
        import Base: to_indices, uncolon, tail, _maybetail
    
        @inline to_indices(A, inds, I::Tuple{Not, Vararg{Any}}) =
           (setdiff(uncolon(inds, (:, tail(I)...)), I[1].idx), to_indices(A, _maybetail(inds), tail(I))...)       
    else
        # 0.5
        import Base: getindex, _getindex
    
        not_index(a::AbstractArray, I, i::Int) = I
        not_index(a::AbstractArray, I::Not, i::Int) = setdiff(indices(a, i), I.idx)
    
        getindex(a::AbstractArray, I::Not) = getindex(a, setdiff(linearindices(a), I.idx))
        _getindex(::Base.LinearIndexing, a::AbstractArray, I::Vararg{Union{Real, AbstractArray, Colon, Not}}) = 
            Base._getindex(Base.linearindexing(a), a, (not_index(a, idx, i) for (i,idx) in enumerate(I))...)
    end
    

    For example:

    julia> a = reshape(1:9, (3, 3))
    3×3 Base.ReshapedArray{Int64,2,UnitRange{Int64},Tuple{}}:
    1  4  7
    2  5  8
    3  6  9
    
    julia> a[Not(2:8)]
    2-element Array{Int64,1}:
    1
    9
    
    julia> a[Not(1:2), :]
    1×3 Array{Int64,2}:
    3  6  9
    
    
    julia> a[Not(end), end]
    2-element Array{Int64,1}:
    7
    8
    

    I didn't care for performance and also did no extensive testing, so things can certainly be improved.

    Edit:

    I replaced the code for 0.6 with Matt B. version from his github comment linked in the comments.

    Thanks to his great design of the array indexing implementation for 0.6, only a single function needs to be extended to get complement indexing for getindex, setindex and view, e.g.,

    julia> view(a, Not(2:8))
    2-element SubArray{Int64,1,UnitRange{Int64},Tuple{Array{Int64,1}},false}:
    1
    9
    
    # collect because ranges are immutable
    julia> b = collect(a); b[Not(2), Not(2)] = 10; b
    3×3 Array{Int64,2}:
    10  4  10
     2  5   8
    10  6  10  
    
    0 讨论(0)
  • 2021-01-18 06:28

    Directly overwriting [](i.e. getindex) is prone to break many indexing-related things in Base, but we can write an array wrapper to work around it. We only need to define the following three methods to get your specific test cases passed:

    immutable ComplementVector{T} <: AbstractArray{T,1}
        data::Vector{T}
    end
    Base.size(A:: ComplementVector) = size(A.data)
    Base.getindex(A:: ComplementVector, i::Integer) = i > 0 ? A.data[i] : A.data[setdiff(1:end, (-i))]
    Base.getindex(A:: ComplementVector, I::StepRange) = all(x->x>0, I) ? A.data[I] : A.data[setdiff(1:end, -I)]
    
    julia> a = ComplementVector([1:8...])
    
    julia> a[-1] == a[2:8]
    true
    
    julia> a[-(1:3)] == a[4:8]
    true
    
    julia> a[-end] == a[1:7]
    true
    

    If you would like to extend ComplementVector further more, please read the doc about Interfaces.

    Update:

    For safety sake, we'd better not extend AbstractArray as @Fengyang Wang suggested in the comment blow:

    immutable ComplementVector{T}
        data::Vector{T}
    end
    Base.endof(A::ComplementVector) = length(A.data)
    Base.getindex(A::ComplementVector, i::Integer) = i > 0 ? A.data[i] : A.data[setdiff(1:end, (-i))]
    Base.getindex(A::ComplementVector, I::OrdinalRange) = all(x->x>0, I) ? A.data[I] : A.data[setdiff(1:end, -I)]
    
    0 讨论(0)
提交回复
热议问题