Skip to content

Commit

Permalink
wip: using Pairs (nalimilan) and merge pred/f functions
Browse files Browse the repository at this point in the history
  • Loading branch information
rfourquet committed Jun 18, 2017
1 parent b6f1398 commit 0298b33
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 65 deletions.
105 changes: 64 additions & 41 deletions base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1994,48 +1994,64 @@ symdiff(a, b, rest...) = symdiff(a, symdiff(b, rest...))
# function _replace!(pred::Callable, new::Callable, A::T, n::Int)

"""
replace!(A, old, new, [n::Integer])
replace!(A, old_new::Pair...; [n::Integer])
Replace all occurrences of `old` in collection `A` by `new`.
If `n` is specified, then replace at most `n` occurrences.
For each pair `old=>new` in `old_new`, replace all occurrences
of `old` in collection `A` by `new`.
If `n` is specified, then replace at most `n` occurrences in total.
See also [`replace`](@ref).
# Examples
```jldoctest
julia> replace!([1, 2, 1, 3], 1, 0, 2)
julia> replace!([1, 2, 1, 3], 1=>0, 2=>4, n=2)
4-element Array{Int64,1}:
0
2
0
4
1
3
julia> replace!(Set([1, 2, 3]), 1, 0)
julia> replace!(Set([1, 2, 3]), 1=>0)
Set([0, 2, 3])
```
"""
replace!(A, old, new, n::Integer=typemax(Int)) = replace!(x->x==old, A, new, n)
function replace!(A, old_new::Pair...; n::Integer=typemax(Int))
# replace!(x->x==old, A, new, n)
replace!(A, n=n) do x
for on in old_new
first(on) == x && return Nullable(last(on))
end
Nullable()
end
end

"""
replace!(pred, A, new, [n::Integer])
replace!(pred, f::Function, A, [n::Integer])
replace!(pred::Function, A, new; [n::Integer])
Replace all occurrences `x` in collection `A` for which `pred(x)` is true
by `new` or `f(x)`.
If `n` is specified, then replace at most `n` occurrences.
by `new`.
# Examples
```jldoctest
julia> a = [1, 2, 3, 1];
julia> A = [1, 2, 3, 1];
julia> replace!(isodd, a, 0, 2); a
julia> replace!(isodd, A, 0, n=2); a
4-element Array{Int64,1}:
0
2
0
1
```
replace!(prednew::Function, A; [n::Integer])
julia> replace!(x->x.first=>3, Dict(1=>2, 3=>4), 1) do kv
first(kv) < 3
For each value `x` in `A`, `prednew(x)` is called and must
return a `Nullable` object. If it is not null, then the wrapped
value will be used as a replacement for `x`.
# Example
```jldoctest
julia> replace!(Dict(1=>2, 3=>4)) do kv
Nullable(first(kv)=>3, first(kv) < 3)
end
Dict{Int64,Int64} with 2 entries:
3 => 4
Expand All @@ -2049,77 +2065,84 @@ Dict{Int64,Int64} with 2 entries:
For example, the following may appear unexpected:
```jldoctest
julia> replace!(x->true, x->2x, Set([3, 6]))
julia> replace!(x->Nullable(2x), Set([3, 6]))
Set([12])
```
"""
function replace!(pred::Callable, new::Callable, A, n::Integer=typemax(Int))
function replace!(prednew::Callable, A; n::Integer=typemax(Int))
n < 0 && throw(DomainError())
n == 0 && return A
_replace!(pred, new, A, clamp(n, 0, typemax(Int)))
_replace!(prednew, A, clamp(n, 0, typemax(Int)))
end

function _replace!(pred::Callable, new::Callable, A::AbstractArray, n::Int)
function _replace!(prednew::Callable, A::AbstractArray, n::Int)
# precondition: n > 0
count = 0
@inbounds for i in eachindex(A)
if pred(A[i])
A[i] = new(A[i])
y = prednew(A[i])
if !isnull(y)
A[i] = get(y)
count += 1
count == n && break
end
end
A
end

replace!(pred::Callable, A, new, n::Integer=typemax(Int)) = replace!(pred, y->new, A, n)
replace!(pred::Callable, A, new; n::Integer=typemax(Int)) =
replace!(x->Nullable(new, pred(x)), A, n=n)

"""
replace(A, old, new, [n])
replace(A, old_new::Pair...; [n::Integer])
Return a copy of collection `A` where all occurrences of `old` are
replaced by `new`.
If `n` is specified, then replace at most `n` occurrences.
Return a copy of collection `A` where, for each pair `old=>new` in `old_new`,
all occurrences of `old` are replaced by `new`.
If `n` is specified, then replace at most `n` occurrences in total.
See also [`replace!`](@ref).
# Examples
```jldoctest
julia> replace([1, 2, 1, 3], 1, 0, 2)
julia> replace([1, 2, 1, 3], 1=>0, 2=>4; n=2)
4-element Array{Int64,1}:
0
2
0
4
1
3
```
"""
replace(A, old, new, n::Integer=typemax(Int)) = replace!(copy(A), old, new, n)
replace(A, old_new::Pair...; n::Integer=typemax(Int)) = replace!(copy(A), old_new..., n=n)

"""
replace(pred, A, new, [n])
replace(pred, f::Callable, A, [n])
replace(pred::Function, A, new; [n::Integer])
Return a copy of collection `A` where all occurrences `x` for which
`pred(x)` is true are replaced by `new` or `f(x)`.
If `n` is specified, then replace at most `n` occurrences.
`pred(x)` is true are replaced by `new`.
# Examples
# Example
```jldoctest
julia> replace(isodd, [1, 2, 3, 1], 0, 2)
julia> replace(isodd, [1, 2, 3, 1], 0, n=2)
4-element Array{Int64,1}:
0
2
0
1
```
replace(prednew::Function, A; [n::Integer])
Return a copy of `A` where for each value `x` in `A`, `prednew(x)` is called
and must return a `Nullable` object. If it is not null, then the wrapped
value will be used as a replacement for `x`.
julia> replace(x->x.first=>3, Dict(1=>2, 3=>4), 1) do kv
first(kv) < 3
julia> replace(Dict(1=>2, 3=>4)) do kv
Nullable(first(kv)=>3, first(kv) < 3)
end
Dict{Int64,Int64} with 2 entries:
3 => 4
1 => 3
```
"""
replace(pred::Callable, new::Callable, A, n::Integer=typemax(Int)) = replace!(pred, new, copy(A), n)
replace(pred::Callable, A, new, n::Integer=typemax(Int)) = replace!(pred, copy(A), new, n)
replace(prednew::Callable, A; n::Integer=typemax(Int)) = replace!(prednew, copy(A), n=n)
replace(pred::Callable, A, new; n::Integer=typemax(Int)) = replace!(pred, copy(A), new, n=n)
15 changes: 8 additions & 7 deletions base/set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -357,20 +357,21 @@ convert(::Type{Set{T}}, x::Set) where {T} = Set{T}(x)
askey(k, ::Associative) = k.first
askey(k, ::AbstractSet) = k

function _replace!(pred::Callable, new::Callable, A::Union{Associative,AbstractSet}, n::Int)
function _replace!(prednew::Callable, A::Union{Associative,AbstractSet}, n::Int)
# precondition: n > 0
del = eltype(A)[]
repl = Pair{eltype(A),eltype(A)}[]
count = 0
for x in A
if pred(x)
push!(del, x)
y = prednew(x)
if !isnull(y)
push!(repl, x=>get(y))
count += 1
count == n && break
end
end
for k in del
pop!(A, askey(k, A))
push!(A, new(k))
for oldnew in repl
pop!(A, askey(first(oldnew), A))
push!(A, last(oldnew))
end
A
end
4 changes: 2 additions & 2 deletions doc/src/stdlib/collections.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ Base.collect(::Type, ::Any)
Base.issubset(::Any, ::Any)
Base.filter
Base.filter!
Base.replace(A, old, new, n::Integer)
Base.replace(pred::Base.Callable, new::Base.Callable, A, n::Integer)
# this also includes the docstring for replace(A, old, new, n)
Base.replace(::Base.Callable, ::Base.Callable, ::Any)
Base.replace!
```

Expand Down
30 changes: 15 additions & 15 deletions test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2152,28 +2152,28 @@ Base.:(==)(a::T11053, b::T11053) = a.a == b.a

@testset "replace! & replace" begin
a = [1, 2, 3, 1]
@test replace(iseven, x->2x, a) == [1, 4, 3, 1]
@test replace!(iseven, x->2x, a) === a
@test replace(x->Nullable(2x, iseven(x)), a) == [1, 4, 3, 1]
@test replace!(x->Nullable(2x, iseven(x)), a) === a
@test a == [1, 4, 3, 1]
@test replace(a, 1, 0) == [0, 4, 3, 0]
@test replace(a, 1, 0, 1) == [0, 4, 3, 1] # 1 replacement only
@test replace!(a, 1, 2) == [2, 4, 3, 2]
@test replace(a, 1=>0) == [0, 4, 3, 0]
@test replace(a, 1=>0, n=1) == [0, 4, 3, 1]
@test replace!(a, 1=>2) == [2, 4, 3, 2]

d = Dict(1=>2, 3=>4)
@test replace(x->x.first > 2, d, 0=>0) == Dict(1=>2, 0=>0)
@test replace!(x->x.first > 2, x->(x.first=>2*x.second), d) ==
@test replace!(x->Nullable(x.first=>2*x.second, x.first > 2), d) ==
Dict(1=>2, 3=>8)
@test replace(d, 3=>8, 0=>0) == Dict(1=>2, 0=>0)
@test replace!(d, 3=>8, 2=>2) === d
@test replace(d, (3=>8)=>(0=>0)) == Dict(1=>2, 0=>0)
@test replace!(d, (3=>8)=>(2=>2)) === d
@test d == Dict(1=>2, 2=>2)
@test replace(x->x.second == 2, d, 0=>0, 1) in [Dict(1=>2, 0=>0),
Dict(2=>2, 0=>0)]
@test replace(x->x.second == 2, d, 0=>0, n=1) in [Dict(1=>2, 0=>0),
Dict(2=>2, 0=>0)]

s = Set([1, 2, 3])
@test replace(x->x>1, x->2x, s) == Set([1, 4, 6])
@test replace(x->x>1, x->2x, s, 1) in [Set([1, 4, 3]), Set([1, 2, 6])]
@test replace(s, 1, 4) == Set([2, 3, 4])
@test replace!(s, 1, 2) == Set([2, 3])
@test replace(x->Nullable(2x, x>1), s) == Set([1, 4, 6])
@test replace(x->Nullable(2x, x>1), s, n=1) in [Set([1, 4, 3]), Set([1, 2, 6])]
@test replace(s, 1=>4) == Set([2, 3, 4])
@test replace!(s, 1=>2) == Set([2, 3])

@test !(0 in replace([1, 2, 3], 1, 0, 0)) # count=0 --> no replacements
@test 0 replace([1, 2, 3], 1=>0, n=0) # count=0 --> no replacements
end

0 comments on commit 0298b33

Please sign in to comment.