From d698d1927af2f7f69f915ab0968c5d74c40e12fa Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Mon, 16 Dec 2013 00:41:31 -0800 Subject: [PATCH 1/2] Implementation of deleteat! --- NEWS.md | 3 ++ base/array.jl | 67 ++++++++++++++++++++++++++++++ base/bitarray.jl | 99 +++++++++++++++++++++++++++++++++++++++++---- base/exports.jl | 1 + doc/stdlib/base.rst | 10 +++++ test/arrayops.jl | 10 +++++ test/bitarray.jl | 64 +++++++++++++++++++++++++---- 7 files changed, 238 insertions(+), 16 deletions(-) diff --git a/NEWS.md b/NEWS.md index 2dfd5cb42ef93..98e4eaabf6fa5 100644 --- a/NEWS.md +++ b/NEWS.md @@ -49,6 +49,9 @@ Library improvements * `modpi` function ([#4799]). + * New function `deleteat!` deletes a specified index or indices and + returns the updated collection + Deprecated or removed --------------------- diff --git a/base/array.jl b/base/array.jl index 54e1baab9c37b..6923f9fa8e3b7 100644 --- a/base/array.jl +++ b/base/array.jl @@ -854,6 +854,73 @@ function splice!{T<:Integer}(a::Vector, r::Range1{T}, ins::AbstractArray=_defaul return v end +function deleteat!(a::Vector, i::Integer) + n = length(a) + if !(1 <= i <= n) + throw(BoundsError()) + end + if i < div(n,2) + for k = i:-1:2 + a[k] = a[k-1] + end + ccall(:jl_array_del_beg, Void, (Any, Uint), a, 1) + else + for k = i:n-1 + a[k] = a[k+1] + end + ccall(:jl_array_del_end, Void, (Any, Uint), a, 1) + end + return a +end + +function deleteat!{T<:Integer}(a::Vector, r::Range1{T}) + n = length(a) + f = first(r) + l = last(r) + if !(1 <= f && l <= n) + throw(BoundsError()) + end + delta = l-f+1 + if f-1 < n-l + for k = l:-1:1+delta + a[k] = a[k-delta] + end + ccall(:jl_array_del_beg, Void, (Any, Uint), a, delta) + else + for k = f:n-delta + a[k] = a[k+delta] + end + ccall(:jl_array_del_end, Void, (Any, Uint), a, delta) + end + return a +end + +function deleteat!(a::Vector, inds) + n = length(a) + s = start(inds) + done(inds, s) && return a + (p, s) = next(inds, s) + q = p+1 + while !done(inds, s) + (i,s) = next(inds, s) + if !(q <= i <= n) + i < q && error("indices must be unique and sorted") + throw(BoundsError()) + end + while q < i + a[p] = a[q] + p += 1; q += 1 + end + q = i+1 + end + while q <= n + a[p] = a[q] + p += 1; q += 1 + end + ccall(:jl_array_del_end, Void, (Any, Uint), a, n-p+1) + return a +end + function empty!(a::Vector) ccall(:jl_array_del_end, Void, (Any, Uint), a, length(a)) return a diff --git a/base/bitarray.jl b/base/bitarray.jl index 072555c9e0959..8b4c99ca703ef 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1049,12 +1049,7 @@ function insert!(B::BitVector, i::Integer, item) B[i] = item end -function splice!(B::BitVector, i::Integer) - n = length(B) - if !(1 <= i <= n) - throw(BoundsError()) - end - v = B[i] +function _deleteat!(B::BitVector, i::Integer) k, j = get_chunks_id(i) @@ -1085,6 +1080,17 @@ function splice!(B::BitVector, i::Integer) B.len -= 1 + return B +end + +function splice!(B::BitVector, i::Integer) + n = length(B) + if !(1 <= i <= n) + throw(BoundsError()) + end + + v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView + _deleteat!(B, i) return v end splice!(B::BitVector, i::Integer, ins::BitVector) = splice!(B, int(i):int(i), ins) @@ -1103,9 +1109,12 @@ function splice!(B::BitVector, r::Range1{Int}, ins::BitVector = _default_bit_spl throw(BoundsError()) end if (i_f > n) - return append!(B, ins) + append!(B, ins) + return BitVector(0) end + v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView + Bc = B.chunks lins = length(ins) @@ -1129,10 +1138,84 @@ function splice!(B::BitVector, r::Range1{Int}, ins::BitVector = _default_bit_spl Bc[end] &= @_msk_end new_l end - return B + return v end splice!(B::BitVector, r::Range1{Int}, ins::AbstractVector{Bool}) = splice!(B, r, bitpack(ins)) +function deleteat!(B::BitVector, i::Integer) + n = length(B) + if !(1 <= i <= n) + throw(BoundsError()) + end + + return _deleteat!(B, i) +end + +function deleteat!(B::BitVector, r::Range1{Int}) + n = length(B) + i_f = first(r) + i_l = last(r) + if !(1 <= i_f && i_l <= n) + throw(BoundsError()) + end + + Bc = B.chunks + new_l = length(B) - length(r) + delta_k = num_bit_chunks(new_l) - length(Bc) + + copy_chunks(Bc, i_f, Bc, i_l+1, n-i_l) + + if delta_k < 0 + ccall(:jl_array_del_end, Void, (Any, Uint), Bc, -delta_k) + end + + B.len = new_l + + if new_l > 0 + Bc[end] &= @_msk_end new_l + end + + return B +end + +function deleteat!(B::BitVector, inds) + n = new_l = length(B) + s = start(inds) + done(inds, s) && return B + + Bc = B.chunks + + (p, s) = next(inds, s) + q = p+1 + new_l -= 1 + while !done(inds, s) + (i,s) = next(inds, s) + if !(q <= i <= n) + i < q && error("indices must be unique and sorted") + throw(BoundsError()) + end + new_l -= 1 + if i > q + copy_chunks(Bc, p, Bc, q, i-q) + p += i-q + end + q = i+1 + end + + q <= n && copy_chunks(Bc, p, Bc, q, n-q+1) + + delta_k = num_bit_chunks(new_l) - length(Bc) + delta_k < 0 && ccall(:jl_array_del_end, Void, (Any, Uint), Bc, -delta_k) + + B.len = new_l + + if new_l > 0 + Bc[end] &= @_msk_end new_l + end + + return B +end + function empty!(B::BitVector) ccall(:jl_array_del_end, Void, (Any, Uint), B.chunks, length(B.chunks)) B.len = 0 diff --git a/base/exports.jl b/base/exports.jl index 48395d6e953a1..9fc400e9d1469 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -693,6 +693,7 @@ export contains, count, delete!, + deleteat!, eltype, empty!, endof, diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index 8553f44ddc476..d017cb59ed0f0 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -827,6 +827,16 @@ Dequeues Insert an item at the given index. +.. function:: deleteat!(collection, index) + + Remove the item at the given index, and return the modified collection. Subsequent items + are shifted to fill the resulting gap. + +.. function:: deleteat!(collection, itr) + + Remove the items at the indices given by `itr`, and return the modified collection. Subsequent + items are shifted to fill the resulting gap. `itr` must be sorted and unique. + .. function:: splice!(collection, index, [replacement]) -> item Remove the item at the given index, and return the removed item. Subsequent items diff --git a/test/arrayops.jl b/test/arrayops.jl index 1263721297bfe..a85fbd3295f8a 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -713,6 +713,16 @@ for idx in {1, 2, 5, 9, 10, 1:0, 2:1, 1:1, 2:2, 1:2, 2:4, 9:8, 10:9, 9:9, 10:10, end end +# deleteat! +for idx in {1, 2, 5, 9, 10, 1:0, 2:1, 1:1, 2:2, 1:2, 2:4, 9:8, 10:9, 9:9, 10:10, + 8:9, 9:10, 6:9, 7:10} + a = [1:10]; acopy = copy(a) + @test deleteat!(a, idx) == [acopy[1:(first(idx)-1)], acopy[(last(idx)+1):end]] +end +a = [1:10] +@test deleteat!(a, [1,3,5,7:10...]) == [2,4,6] + + # comprehensions X = [ i+2j for i=1:5, j=1:5 ] @test X[2,3] == 8 diff --git a/test/bitarray.jl b/test/bitarray.jl index 62c4931fbd016..9964783b03540 100644 --- a/test/bitarray.jl +++ b/test/bitarray.jl @@ -190,8 +190,19 @@ b1 = randbool(v1) i1 = bitunpack(b1) for m = v1 : -1 : 1 j = rand(1:m) - splice!(b1, j) - splice!(i1, j) + b = splice!(b1, j) + i = splice!(i1, j) + @test isequal(bitunpack(b1), i1) + @test b == i +end +@test length(b1) == 0 + +b1 = randbool(v1) +i1 = bitunpack(b1) +for m = v1 : -1 : 1 + j = rand(1:m) + deleteat!(b1, j) + deleteat!(i1, j) @test isequal(bitunpack(b1), i1) end @test length(b1) == 0 @@ -199,9 +210,31 @@ end b1 = randbool(v1) i1 = bitunpack(b1) for j in [63, 64, 65, 127, 128, 129, 191, 192, 193] - splice!(b1, j) - splice!(i1, j) + b = splice!(b1, j) + i = splice!(i1, j) @test isequal(bitunpack(b1), i1) + @test b == i +end + +b1 = randbool(v1) +i1 = bitunpack(b1) +for j in [63, 64, 65, 127, 128, 129, 191, 192, 193] + deleteat!(b1, j) + deleteat!(i1, j) + @test isequal(bitunpack(b1), i1) +end + +b1 = randbool(v1) +i1 = bitunpack(b1) +for m1 = 1 : v1 + for m2 = m1 : v1 + b2 = copy(b1) + i2 = copy(i1) + b = splice!(b2, m1:m2) + i = splice!(i2, m1:m2) + @test isequal(bitunpack(b2), i2) + @test b == i + end end b1 = randbool(v1) @@ -210,8 +243,8 @@ for m1 = 1 : v1 for m2 = m1 : v1 b2 = copy(b1) i2 = copy(i1) - splice!(b2, m1:m2) - splice!(i2, m1:m2) + deleteat!(b2, m1:m2) + deleteat!(i2, m1:m2) @test isequal(bitunpack(b2), i2) end end @@ -225,13 +258,28 @@ for m1 = 1 : v1 + 1 i2 = copy(i1) b3 = randbool(v2) i3 = bitunpack(b3) - splice!(b2, m1:m2, b3) - splice!(i2, m1:m2, i3) + b = splice!(b2, m1:m2, b3) + i = splice!(i2, m1:m2, i3) @test isequal(bitunpack(b2), i2) + @test b == i end end end +b1 = randbool(v1) +i1 = bitunpack(b1) +for m1 = 1 : v1 - 1 + for m2 = m1 + 1 : v1 + locs = randbool(m2-m1+1) + m = [m1:m2...][locs] + b2 = copy(b1) + i2 = copy(i1) + deleteat!(b2, m) + deleteat!(i2, m) + @test isequal(bitunpack(b2), i2) + end +end + b1 = randbool(v1) i1 = bitunpack(b1) empty!(b1) From fc157019ab662b93adfa49d7a16538bd22d24cbf Mon Sep 17 00:00:00 2001 From: Kevin Squire Date: Tue, 17 Dec 2013 08:40:29 -0800 Subject: [PATCH 2/2] Refactor splice!, delete! --- base/array.jl | 241 +++++++++++++++++++++++------------------------ base/bitarray.jl | 118 +++++++++++------------ 2 files changed, 177 insertions(+), 182 deletions(-) diff --git a/base/array.jl b/base/array.jl index 6923f9fa8e3b7..ce0fb17b5195b 100644 --- a/base/array.jl +++ b/base/array.jl @@ -654,6 +654,65 @@ setindex!(A::Array, x, I::AbstractVector{Bool}, J::AbstractVector{Bool}) = setin setindex!{T<:Real}(A::Array, x, I::AbstractVector{T}, J::AbstractVector{Bool}) = setindex!(A, x, I,find(J)) setindex!{T<:Real}(A::Array, x, I::AbstractVector{Bool}, J::AbstractVector{T}) = setindex!(A, x, find(I),J) +# efficiently grow an array + +function _growat!(a::Vector, i::Integer, delta::Integer) + n = length(a) + if i < div(n,2) + _growat_beg!(a, i, delta) + else + _growat_end!(a, i, delta) + end + return a +end + +function _growat_beg!(a::Vector, i::Integer, delta::Integer) + ccall(:jl_array_grow_beg, Void, (Any, Uint), a, delta) + @inbounds for k = 1:i-1 + a[k] = a[k+delta] + end + return a +end + +function _growat_end!(a::Vector, i::Integer, delta::Integer) + ccall(:jl_array_grow_end, Void, (Any, Uint), a, delta) + n = length(a) + @inbounds for k = n:-1:(i+delta) + a[k] = a[k-delta] + end + return a +end + +# efficiently delete part of an array + +function _deleteat!(a::Vector, i::Integer, delta::Integer) + n = length(a) + last = i+delta-1 + if i-1 < n-last + _deleteat_beg!(a, i, delta) + else + _deleteat_end!(a, i, delta) + end + return a +end + +function _deleteat_beg!(a::Vector, i::Integer, delta::Integer) + @inbounds for k = i+delta-1:-1:1+delta + a[k] = a[k-delta] + end + ccall(:jl_array_del_beg, Void, (Any, Uint), a, delta) + return a +end + +function _deleteat_end!(a::Vector, i::Integer, delta::Integer) + n = length(a) + @inbounds for k = i:n-delta + a[k] = a[k+delta] + end + ccall(:jl_array_del_end, Void, (Any, Uint), a, delta) + return a +end + ## Dequeue functionality ## const _grow_none_errmsg = @@ -754,123 +813,18 @@ function insert!{T}(a::Array{T,1}, i::Integer, item) n = length(a) if i > n ccall(:jl_array_grow_end, Void, (Any, Uint), a, i-n) - elseif i > div(n,2) - ccall(:jl_array_grow_end, Void, (Any, Uint), a, 1) - for k=n+1:-1:i+1 - a[k] = a[k-1] - end else - ccall(:jl_array_grow_beg, Void, (Any, Uint), a, 1) - for k=1:(i-1) - a[k] = a[k+1] - end + _growat!(a, i, 1) end a[i] = item return a end -const _default_splice = [] - -function splice!(a::Vector, i::Integer, ins::AbstractArray=_default_splice) - n = length(a) - if !(1 <= i <= n) - throw(BoundsError()) - end - v = a[i] - m = length(ins) - if m == 0 - if i < div(n,2) - for k = i:-1:2 - a[k] = a[k-1] - end - ccall(:jl_array_del_beg, Void, (Any, Uint), a, 1) - else - for k = i:n-1 - a[k] = a[k+1] - end - ccall(:jl_array_del_end, Void, (Any, Uint), a, 1) - end - elseif m == 1 - a[i] = ins[1] - else - if i < div(n,2) - ccall(:jl_array_grow_beg, Void, (Any, Uint), a, m-1) - for k = 1:i-1 - a[k] = a[k+m-1] - end - else - ccall(:jl_array_grow_end, Void, (Any, Uint), a, m-1) - for k = n+m-1:-1:(i+1+(m-1)) - a[k] = a[k-(m-1)] - end - end - for k = 1:m - a[i+k-1] = ins[k] - end - end - return v -end - -function splice!{T<:Integer}(a::Vector, r::Range1{T}, ins::AbstractArray=_default_splice) - n = length(a) - f = first(r) - l = last(r) - if !(1 <= f && l <= n) - throw(BoundsError()) - end - d = l-f+1 - v = a[r] - m = length(ins) - if m < d - delta = d - m - if f-1 < n-l - for k = l:-1:1+delta - a[k] = a[k-delta] - end - ccall(:jl_array_del_beg, Void, (Any, Uint), a, delta) - else - for k = f:n-delta - a[k] = a[k+delta] - end - ccall(:jl_array_del_end, Void, (Any, Uint), a, delta) - end - elseif m > d - delta = m - d - if f-1 < n-l - ccall(:jl_array_grow_beg, Void, (Any, Uint), a, delta) - for k = 1:f-1 - a[k] = a[k+delta] - end - else - ccall(:jl_array_grow_end, Void, (Any, Uint), a, delta) - for k = n+delta:-1:(l+1+delta) - a[k] = a[k-delta] - end - end - end - for k = 1:m - a[f+k-1] = ins[k] - end - return v -end - function deleteat!(a::Vector, i::Integer) - n = length(a) - if !(1 <= i <= n) + if !(1 <= i <= length(a)) throw(BoundsError()) end - if i < div(n,2) - for k = i:-1:2 - a[k] = a[k-1] - end - ccall(:jl_array_del_beg, Void, (Any, Uint), a, 1) - else - for k = i:n-1 - a[k] = a[k+1] - end - ccall(:jl_array_del_end, Void, (Any, Uint), a, 1) - end - return a + return _deleteat!(a, i, 1) end function deleteat!{T<:Integer}(a::Vector, r::Range1{T}) @@ -880,19 +834,7 @@ function deleteat!{T<:Integer}(a::Vector, r::Range1{T}) if !(1 <= f && l <= n) throw(BoundsError()) end - delta = l-f+1 - if f-1 < n-l - for k = l:-1:1+delta - a[k] = a[k-delta] - end - ccall(:jl_array_del_beg, Void, (Any, Uint), a, delta) - else - for k = f:n-delta - a[k] = a[k+delta] - end - ccall(:jl_array_del_end, Void, (Any, Uint), a, delta) - end - return a + return _deleteat!(a, f, length(r)) end function deleteat!(a::Vector, inds) @@ -908,19 +850,72 @@ function deleteat!(a::Vector, inds) throw(BoundsError()) end while q < i - a[p] = a[q] + @inbounds a[p] = a[q] p += 1; q += 1 end q = i+1 end while q <= n - a[p] = a[q] + @inbounds a[p] = a[q] p += 1; q += 1 end ccall(:jl_array_del_end, Void, (Any, Uint), a, n-p+1) return a end +const _default_splice = [] + +function splice!(a::Vector, i::Integer, ins::AbstractArray=_default_splice) + v = a[i] + m = length(ins) + if m == 0 + _deleteat!(a, i, 1) + elseif m == 1 + a[i] = ins[1] + else + _growat!(a, i, m-1) + for k = 1:m + a[i+k-1] = ins[k] + end + end + return v +end + +function splice!{T<:Integer}(a::Vector, r::Range1{T}, ins::AbstractArray=_default_splice) + v = a[r] + m = length(ins) + if m == 0 + deleteat!(a, r) + return v + end + + n = length(a) + f = first(r) + l = last(r) + d = length(r) + + if m < d + delta = d - m + if f-1 < n-l + _deleteat_beg!(a, f, delta) + else + _deleteat_end!(a, l-delta+1, delta) + end + elseif m > d + delta = m - d + if f-1 < n-l + _growat_beg!(a, f, delta) + else + _growat_end!(a, l+1, delta) + end + end + + for k = 1:m + a[f+k-1] = ins[k] + end + return v +end + function empty!(a::Vector) ccall(:jl_array_del_end, Void, (Any, Uint), a, length(a)) return a diff --git a/base/bitarray.jl b/base/bitarray.jl index 8b4c99ca703ef..c7e6a0d38379c 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -1083,65 +1083,6 @@ function _deleteat!(B::BitVector, i::Integer) return B end -function splice!(B::BitVector, i::Integer) - n = length(B) - if !(1 <= i <= n) - throw(BoundsError()) - end - - v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView - _deleteat!(B, i) - return v -end -splice!(B::BitVector, i::Integer, ins::BitVector) = splice!(B, int(i):int(i), ins) -splice!(B::BitVector, i::Integer, ins::AbstractVector{Bool}) = splice!(B, i, bitpack(ins)) - -const _default_bit_splice = BitVector(0) - -function splice!(B::BitVector, r::Range1{Int}, ins::BitVector = _default_bit_splice) - n = length(B) - i_f = first(r) - i_l = last(r) - if !(1 <= i_f <= n+1) - throw(BoundsError()) - end - if !(i_l <= n) - throw(BoundsError()) - end - if (i_f > n) - append!(B, ins) - return BitVector(0) - end - - v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView - - Bc = B.chunks - - lins = length(ins) - ldel = length(r) - - new_l = length(B) + lins - ldel - delta_k = num_bit_chunks(new_l) - length(Bc) - - if delta_k > 0 - ccall(:jl_array_grow_end, Void, (Any, Uint), Bc, delta_k) - end - copy_chunks(Bc, i_f+lins, Bc, i_l+1, n-i_l) - copy_chunks(Bc, i_f, ins.chunks, 1, lins) - if delta_k < 0 - ccall(:jl_array_del_end, Void, (Any, Uint), Bc, -delta_k) - end - - B.len = new_l - - if new_l > 0 - Bc[end] &= @_msk_end new_l - end - - return v -end -splice!(B::BitVector, r::Range1{Int}, ins::AbstractVector{Bool}) = splice!(B, r, bitpack(ins)) - function deleteat!(B::BitVector, i::Integer) n = length(B) if !(1 <= i <= n) @@ -1216,6 +1157,65 @@ function deleteat!(B::BitVector, inds) return B end +function splice!(B::BitVector, i::Integer) + n = length(B) + if !(1 <= i <= n) + throw(BoundsError()) + end + + v = B[i] # TODO: change to a copy if/when subscripting becomes an ArrayView + _deleteat!(B, i) + return v +end +splice!(B::BitVector, i::Integer, ins::BitVector) = splice!(B, int(i):int(i), ins) +splice!(B::BitVector, i::Integer, ins::AbstractVector{Bool}) = splice!(B, i, bitpack(ins)) + +const _default_bit_splice = BitVector(0) + +function splice!(B::BitVector, r::Range1{Int}, ins::BitVector = _default_bit_splice) + n = length(B) + i_f = first(r) + i_l = last(r) + if !(1 <= i_f <= n+1) + throw(BoundsError()) + end + if !(i_l <= n) + throw(BoundsError()) + end + if (i_f > n) + append!(B, ins) + return BitVector(0) + end + + v = B[r] # TODO: change to a copy if/when subscripting becomes an ArrayView + + Bc = B.chunks + + lins = length(ins) + ldel = length(r) + + new_l = length(B) + lins - ldel + delta_k = num_bit_chunks(new_l) - length(Bc) + + if delta_k > 0 + ccall(:jl_array_grow_end, Void, (Any, Uint), Bc, delta_k) + end + copy_chunks(Bc, i_f+lins, Bc, i_l+1, n-i_l) + copy_chunks(Bc, i_f, ins.chunks, 1, lins) + if delta_k < 0 + ccall(:jl_array_del_end, Void, (Any, Uint), Bc, -delta_k) + end + + B.len = new_l + + if new_l > 0 + Bc[end] &= @_msk_end new_l + end + + return v +end +splice!(B::BitVector, r::Range1{Int}, ins::AbstractVector{Bool}) = splice!(B, r, bitpack(ins)) + function empty!(B::BitVector) ccall(:jl_array_del_end, Void, (Any, Uint), B.chunks, length(B.chunks)) B.len = 0