-
-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Functional non-mutating methods. #46453
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
895dc59
7461359
94eb379
495ab02
44eba7e
c05019b
82af253
d7b0673
230e352
ee924a1
f7e9888
2e40121
94191a8
b5df3c7
3ca433a
57498dd
27304e0
abb003e
cd5a701
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1007,6 +1007,64 @@ function setindex!(A::Array{T}, X::Array{T}, c::Colon) where T | |
return A | ||
end | ||
|
||
""" | ||
setindex(collection, value, key...) | ||
|
||
Create a new collection with the element/elements of the location specified by `key` | ||
replaced with `value`(s). | ||
|
||
Note that this may be particularly expensive when `collection` is large and whose storage | ||
cannot be shared between seperate instances. | ||
|
||
# Implementation | ||
|
||
`setindex(collection, value, key...)` must have the property such that | ||
|
||
```julia | ||
y1 = setindex(x, value, key...) | ||
|
||
y2 = copy′(x) | ||
@assert convert.(eltype(y2), x) == y2 | ||
|
||
y2[key...] = value | ||
@assert convert.(eltype(y2), y1) == y2 | ||
``` | ||
|
||
with a suitable definition of `copy′` such that `y2[key...] = value` succeeds. | ||
|
||
`setindex` should support more combinations of arguments by widening collection | ||
type as required. | ||
""" | ||
function setindex end | ||
|
||
function setindex(x::AbstractArray, v, i...) | ||
@_propagate_inbounds_meta | ||
inds = to_indices(x, i) | ||
if inds isa Tuple{Vararg{Integer}} | ||
T = promote_type(eltype(x), typeof(v)) | ||
else | ||
T = promote_type(eltype(x), eltype(v)) | ||
end | ||
y = similar(x, T) | ||
copy!(y, x) | ||
y[inds...] = v | ||
return y | ||
end | ||
function setindex(t::Tuple, v, inds::AbstractUnitRange{<:Integer}) | ||
Tokazama marked this conversation as resolved.
Show resolved
Hide resolved
|
||
@boundscheck checkindex(Bool, eachindex(t), inds) || throw(BoundsError(t, inds)) | ||
@boundscheck setindex_shape_check(v, length(inds)) | ||
start = first(inds) | ||
stop = last(inds) | ||
offset = start - firstindex(v) | ||
ntuple(Val{nfields(t)}()) do i | ||
if i < start || i > stop | ||
getfield(t, i) | ||
else | ||
v[i - offset] | ||
end | ||
end | ||
end | ||
|
||
# efficiently grow an array | ||
|
||
_growbeg!(a::Vector, delta::Integer) = | ||
|
@@ -1145,6 +1203,63 @@ function _append!(a, ::IteratorSize, iter) | |
a | ||
end | ||
|
||
""" | ||
insert(a::AbstractVector, index::Integer, item) | ||
|
||
Returns a copy of `a` with `item` inserted at `index`. | ||
|
||
See also: [`insert!`](@ref), [`deleteat`](@ref), [`delete`](@ref) | ||
|
||
# Examples | ||
```jldoctest | ||
julia> A = Any[1, 2, 3] | ||
3-element Vector{Any}: | ||
1 | ||
2 | ||
3 | ||
|
||
julia> insert(A, 3, "here") == [1, 2, "here", 3] | ||
true | ||
|
||
julia> A == [1, 2, 3] | ||
true | ||
```` | ||
""" | ||
function insert(src::AbstractVector, index::Integer, item) | ||
src_idx = firstindex(src) | ||
src_end = lastindex(src) | ||
if index == (src_end + 1) | ||
dst = similar(src, promote_type(eltype(src), typeof(item)), length(src) + 1) | ||
for dst_idx in eachindex(dst) | ||
@inbounds if isassigned(src, src_idx) | ||
@inbounds dst[dst_idx] = src[src_idx] | ||
end | ||
src_idx += 1 | ||
end | ||
@inbounds dst[end] = item | ||
else | ||
@boundscheck (src_end < index || index < src_idx) && throw(BoundsError(src, index)) | ||
dst = similar(src, promote_type(eltype(src), typeof(item)), length(src) + 1) | ||
dst_idx = firstindex(dst) | ||
while src_idx < index | ||
@inbounds if isassigned(src, src_idx) | ||
@inbounds dst[dst_idx] = src[src_idx] | ||
end | ||
dst_idx += 1 | ||
src_idx += 1 | ||
end | ||
setindex!(dst, item, dst_idx) | ||
dst_idx += 1 | ||
for i in src_idx:src_end | ||
@inbounds if isassigned(src, i) | ||
@inbounds dst[dst_idx] = src[i] | ||
end | ||
dst_idx += 1 | ||
end | ||
end | ||
dst | ||
end | ||
|
||
""" | ||
prepend!(a::Vector, collections...) -> collection | ||
|
||
|
@@ -1633,6 +1748,117 @@ function deleteat!(a::Vector, inds::AbstractVector{Bool}) | |
return a | ||
end | ||
|
||
""" | ||
deleteat(a::Vector, i::Integer) | ||
|
||
Remove the item at the given `i` and return the modified `a`. Subsequent items | ||
are shifted to fill the resulting gap. | ||
|
||
See also: [`deleteat!`](@ref), [`delete`](@ref), [`insert`](@ref) | ||
|
||
# Examples | ||
```jldoctest | ||
julia> x = [6, 5, 4] | ||
3-element Vector{Int64}: | ||
6 | ||
5 | ||
4 | ||
|
||
julia> deleteat(x, 2) == [6, 4] | ||
true | ||
|
||
julia> deleteat(x, [2, 3]) == [6] | ||
true | ||
|
||
julia> x == [6, 5, 4] | ||
true | ||
``` | ||
""" | ||
function deleteat(src::AbstractVector, i::Integer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. deleteat(src::AbstractVector, i) = deleteat!(copy(src), i) ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see - it may work with setindex-able, but not resizeable arrays as of now... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's a bit messy. I thought about putting |
||
src_idx = firstindex(src) | ||
src_end = lastindex(src) | ||
src_idx <= i <= src_end || throw(BoundsError(src, i)) | ||
dst = similar(src, length(src) - 1) | ||
dst_idx = firstindex(dst) | ||
while src_idx <= src_end | ||
if src_idx != i | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
dst_idx += 1 | ||
end | ||
src_idx += 1 | ||
end | ||
dst | ||
end | ||
function deleteat(src::AbstractVector, inds::AbstractVector{Bool}) | ||
n = length(src) | ||
length(inds) == n || throw(BoundsError(src, inds)) | ||
dst = similar(src, n - count(inds)) | ||
dst_idx = firstindex(dst) | ||
src_idx = firstindex(src) | ||
for index in inds | ||
if !index | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
dst_idx += 1 | ||
end | ||
src_idx += 1 | ||
end | ||
dst | ||
end | ||
function deleteat(src::AbstractVector, inds::AbstractUnitRange{<:Integer}) | ||
srcinds = eachindex(src) | ||
N = length(srcinds) | ||
dst = similar(src, N - length(inds)) | ||
src_idx = first(srcinds) | ||
dst_idx = firstindex(dst) | ||
while src_idx < first(inds) | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
src_idx += 1 | ||
dst_idx += 1 | ||
end | ||
src_idx = last(inds) + 1 | ||
while src_idx <= N | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
src_idx += 1 | ||
dst_idx += 1 | ||
end | ||
dst | ||
end | ||
function deleteat(src::AbstractVector, inds) | ||
itr = iterate(inds) | ||
if itr === nothing | ||
copy(src) | ||
else | ||
len = length(src) - length(inds) | ||
# `length(inds) > length(src)` then `inds` has repeated or out of bounds indices | ||
len > 0 || throw(BoundsError(src, inds)) | ||
index, state = itr | ||
dst = similar(src, len) | ||
dst_idx = firstindex(dst) | ||
src_idx = firstindex(src) | ||
src_end = lastindex(src) | ||
src_idx > index && throw(BoundsError(src, inds)) | ||
while true | ||
(src_end < index || index < src_idx) && throw(BoundsError(src, inds)) | ||
while src_idx < index | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
dst_idx += 1 | ||
src_idx += 1 | ||
end | ||
src_idx += 1 | ||
itr = iterate(inds, state) | ||
itr === nothing && break | ||
itr[1] <= index && throw(ArgumentError("indices must be unique and sorted")) | ||
index, state = itr | ||
end | ||
while src_idx <= src_end | ||
@inbounds isassigned(src, src_idx) && setindex!(dst, src[src_idx], dst_idx) | ||
dst_idx += 1 | ||
src_idx += 1 | ||
end | ||
dst | ||
end | ||
end | ||
|
||
const _default_splice = [] | ||
|
||
""" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -395,7 +395,6 @@ function setindex!(h::Dict{K,Any}, v, key::K) where K | |
return h | ||
end | ||
|
||
|
||
""" | ||
get!(collection, key, default) | ||
|
||
|
@@ -669,6 +668,30 @@ function delete!(h::Dict, key) | |
return h | ||
end | ||
|
||
""" | ||
delete(collection, key) | ||
|
||
Create and return a new collection containing all the elements from `collection` except for | ||
the mapping corresponding to `key`. | ||
|
||
Note that this may be particularly expensive when `collection` is large and whose storage | ||
cannot be shared between seperate instances. | ||
|
||
See also: [`delete!`](@ref). | ||
|
||
# Examples | ||
```jldoctest | ||
julia> d = Dict("a"=>1, "b"=>2); | ||
|
||
julia> delete(d, "b") == Dict("a"=>1) | ||
true | ||
|
||
julia> d == Dict("a"=>1, "b"=>2) | ||
true | ||
``` | ||
""" | ||
delete(collection, k) = delete!(copy(collection), k) | ||
|
||
function skip_deleted(h::Dict, i) | ||
L = length(h.slots) | ||
for i = i:L | ||
|
@@ -821,6 +844,39 @@ function get(default::Callable, dict::ImmutableDict, key) | |
return default() | ||
end | ||
|
||
function delete(d::ImmutableDict{K,V}, key) where {K,V} | ||
if isdefined(d, :parent) | ||
if isequal(d.key, key) | ||
d.parent | ||
else | ||
ImmutableDict{K,V}(delete(d.parent, key), d.key, d.value) | ||
end | ||
else | ||
d | ||
end | ||
end | ||
Comment on lines
+847
to
+857
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implementation is not quite correct since I can current;y construct an ImmutableDict where there are multiple entries for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I really like that idea, but I think we would need to have a new type since we can't rely on |
||
|
||
function setindex(d::ImmutableDict, v, k) | ||
if isdefined(d, :parent) | ||
if isequal(d.key, k) | ||
d0 = d.parent | ||
v0 = v | ||
k0 = k | ||
else | ||
d0 = setindex(d.parent, v, k) | ||
v0 = d.value | ||
k0 = d.key | ||
end | ||
else | ||
d0 = d | ||
v0 = v | ||
k0 = k | ||
end | ||
K = promote_type(keytype(d0), typeof(k0)) | ||
V = promote_type(valtype(d0), typeof(v0)) | ||
ImmutableDict{K,V}(d0, k0, v0) | ||
end | ||
|
||
# this actually defines reverse iteration (e.g. it should not be used for merge/copy/filter type operations) | ||
function iterate(d::ImmutableDict{K,V}, t=d) where {K, V} | ||
!isdefined(t, :parent) && return nothing | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mutationg