Skip to content

Commit dce9d05

Browse files
rfourquetnalimilan
authored andcommitted
replace the API of replace (#25697)
Make nothing not special.
1 parent ecb8b56 commit dce9d05

File tree

2 files changed

+71
-116
lines changed

2 files changed

+71
-116
lines changed

base/set.jl

+33-65
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ convert(::Type{T}, s::AbstractSet) where {T<:AbstractSet} = T(s)
565565
## replace/replace! ##
566566

567567
# to make replace/replace! work for a new container type Cont, only
568-
# replace!(prednew::Callable, A::Cont; count::Integer=typemax(Int))
568+
# replace!(new::Callable, A::Cont; count::Integer=typemax(Int))
569569
# has to be implemented
570570

571571
"""
@@ -591,15 +591,14 @@ Set([0, 2, 3])
591591
"""
592592
replace!(A, old_new::Pair...; count::Integer=typemax(Int)) = _replace!(A, eltype(A), count, old_new)
593593

594-
# we use this wrapper because using directly eltype(A) as the type
595-
# parameter below for Some degrades performance
596594
function _replace!(A, ::Type{K}, count::Integer, old_new::Tuple{Vararg{Pair}}) where K
597-
@inline function prednew(x)
595+
@inline function new(x)
598596
for o_n in old_new
599-
first(o_n) == x && return Some{K}(last(o_n))
597+
first(o_n) == x && return last(o_n)
600598
end
599+
return x # no replace
601600
end
602-
replace!(prednew, A, count=count)
601+
replace!(new, A, count=count)
603602
end
604603

605604
"""
@@ -621,37 +620,26 @@ julia> replace!(isodd, A, 0, count=2)
621620
```
622621
"""
623622
replace!(pred::Callable, A, new; count::Integer=typemax(Int)) =
624-
replace!(x -> if pred(x) Some(new) end, A, count=count)
623+
replace!(x -> ifelse(pred(x), new, x), A, count=count)
625624

626625
"""
627-
replace!(prednew::Function, A; [count::Integer])
626+
replace!(new::Function, A; [count::Integer])
628627
629-
For each value `x` in `A`, `prednew(x)` is called and must
630-
return either `nothing`, in which case no replacement occurs,
631-
or a value, possibly wrapped as a [`Some`](@ref) object, which
632-
will be used as a replacement for `x`.
628+
Replace each element `x` in collection `A` by `new(x)`.
629+
If `count` is specified, then replace at most `count` values in total
630+
(replacements being defined as `new(x) !== x`).
633631
634632
# Examples
635633
```jldoctest
636-
julia> replace!(x -> isodd(x) ? 2x : nothing, [1, 2, 3, 4])
634+
julia> replace!(x -> isodd(x) ? 2x : x, [1, 2, 3, 4])
637635
4-element Array{Int64,1}:
638636
2
639637
2
640638
6
641639
4
642640
643-
julia> replace!(Union{Int,Nothing}[0, 1, 2, nothing, 4], count=2) do x
644-
x !== nothing && iseven(x) ? Some(nothing) : nothing
645-
end
646-
5-element Array{Union{Nothing,Int64},1}:
647-
nothing
648-
1
649-
nothing
650-
nothing
651-
4
652-
653641
julia> replace!(Dict(1=>2, 3=>4)) do kv
654-
if first(kv) < 3; first(kv)=>3 end
642+
first(kv) < 3 ? first(kv)=>3 : kv
655643
end
656644
Dict{Int64,Int64} with 2 entries:
657645
3 => 4
@@ -661,10 +649,10 @@ julia> replace!(x->2x, Set([3, 6]))
661649
Set([6, 12])
662650
```
663651
"""
664-
function replace!(prednew::Callable, A::Union{AbstractArray,AbstractDict,AbstractSet};
652+
function replace!(new::Callable, A::Union{AbstractArray,AbstractDict,AbstractSet};
665653
count::Integer=typemax(Int))
666654
count < 0 && throw(DomainError(count, "`count` must not be negative"))
667-
count != 0 && _replace!(prednew, A, min(count, typemax(Int)) % Int)
655+
count != 0 && _replace!(new, A, min(count, typemax(Int)) % Int)
668656
A
669657
end
670658

@@ -706,44 +694,31 @@ julia> replace(isodd, [1, 2, 3, 1], 0, count=2)
706694
```
707695
"""
708696
replace(pred::Callable, A, new; count::Integer=typemax(Int)) =
709-
replace!(x -> if pred(x) Some(new) end, copy(A), count=count)
697+
replace!(pred, copy(A), new, count=count)
710698

711699
"""
712-
replace(prednew::Function, A; [count::Integer])
700+
replace(new::Function, A; [count::Integer])
713701
714-
Return a copy of `A` where for each value `x` in `A`, `prednew(x)` is called
715-
and must return either `nothing`, in which case no replacement occurs,
716-
or a value, possibly wrapped as a [`Some`](@ref) object, which
717-
will be used as a replacement for `x`.
702+
Return a copy of `A` where each value `x` in `A` is replaced by `new(x)`
718703
719704
# Examples
720705
```jldoctest
721-
julia> replace(x -> isodd(x) ? 2x : nothing, [1, 2, 3, 4])
706+
julia> replace(x -> isodd(x) ? 2x : x, [1, 2, 3, 4])
722707
4-element Array{Int64,1}:
723708
2
724709
2
725710
6
726711
4
727712
728-
julia> replace(Union{Int,Nothing}[0, 1, 2, nothing, 4], count=2) do x
729-
x !== nothing && iseven(x) ? Some(nothing) : nothing
730-
end
731-
5-element Array{Union{Nothing,Int64},1}:
732-
nothing
733-
1
734-
nothing
735-
nothing
736-
4
737-
738713
julia> replace(Dict(1=>2, 3=>4)) do kv
739-
if first(kv) < 3; first(kv)=>3 end
714+
first(kv) < 3 ? first(kv)=>3 : kv
740715
end
741716
Dict{Int64,Int64} with 2 entries:
742717
3 => 4
743718
1 => 3
744719
```
745720
"""
746-
replace(prednew::Callable, A; count::Integer=typemax(Int)) = replace!(prednew, copy(A), count=count)
721+
replace(new::Callable, A; count::Integer=typemax(Int)) = replace!(new, copy(A), count=count)
747722

748723
# Handle ambiguities
749724
replace!(a::Callable, b::Pair; count::Integer=-1) = throw(MethodError(replace!, (a, b)))
@@ -758,19 +733,15 @@ replace(a::AbstractString, b::Pair, c::Pair) = throw(MethodError(replace, (a, b,
758733
askey(k, ::AbstractDict) = k.first
759734
askey(k, ::AbstractSet) = k
760735

761-
function _replace_update_dict!(repl::Vector{<:Pair}, x, y::Some)
762-
push!(repl, x => y.value)
763-
true
764-
end
765-
766-
_replace_update_dict!(repl::Vector{<:Pair}, x, ::Nothing) = false
767-
_replace_update_dict!(repl::Vector{<:Pair}, x, y) = _replace_update_dict!(repl, x, Some(y))
768-
769-
function _replace!(prednew::Callable, A::Union{AbstractDict,AbstractSet}, count::Int)
736+
function _replace!(new::Callable, A::Union{AbstractDict,AbstractSet}, count::Int)
770737
repl = Pair{eltype(A),eltype(A)}[]
771738
c = 0
772739
for x in A
773-
c += _replace_update_dict!(repl, x, prednew(x))
740+
y = new(x)
741+
if x !== y
742+
push!(repl, x => y)
743+
c += 1
744+
end
774745
c == count && break
775746
end
776747
for oldnew in repl
@@ -783,18 +754,15 @@ end
783754

784755
### AbstractArray
785756

786-
function _replace_update!(A::AbstractArray, i::Integer, y::Some)
787-
@inbounds A[i] = y.value
788-
true
789-
end
790-
791-
_replace_update!(A::AbstractArray, i::Integer, ::Nothing) = false
792-
_replace_update!(A::AbstractArray, i::Integer, y) = _replace_update!(A, i, Some(y))
793-
794-
function _replace!(prednew::Callable, A::AbstractArray, count::Int)
757+
function _replace!(new::Callable, A::AbstractArray, count::Int)
795758
c = 0
796759
for i in eachindex(A)
797-
c += _replace_update!(A, i, prednew(A[i]))
760+
@inbounds Ai = A[i]
761+
y = new(Ai)
762+
if Ai !== y
763+
@inbounds A[i] = y
764+
c += 1
765+
end
798766
c == count && break
799767
end
800768
end

test/sets.jl

+38-51
Original file line numberDiff line numberDiff line change
@@ -492,65 +492,52 @@ end
492492
end
493493

494494
@testset "replace! & replace" begin
495-
maybe1(v, p) = if p Some(v) end
496-
maybe2(v, p) = if p v end
497-
498-
for maybe = (maybe1, maybe2)
499-
a = [1, 2, 3, 1]
500-
@test replace(x->maybe(2x, iseven(x)), a) == [1, 4, 3, 1]
501-
@test replace!(x->maybe(2x, iseven(x)), a) === a
502-
@test a == [1, 4, 3, 1]
503-
@test replace(a, 1=>0) == [0, 4, 3, 0]
504-
for count = (1, 0x1, big(1))
505-
@test replace(a, 1=>0, count=count) == [0, 4, 3, 1]
506-
end
507-
@test replace!(a, 1=>2) === a
508-
@test a == [2, 4, 3, 2]
509-
@test replace!(x->2x, a, count=0x2) == [4, 8, 3, 2]
510-
511-
d = Dict(1=>2, 3=>4)
512-
@test replace(x->x.first > 2, d, 0=>0) == Dict(1=>2, 0=>0)
513-
@test replace!(x->maybe(x.first=>2*x.second, x.first > 2), d) === d
514-
@test d == Dict(1=>2, 3=>8)
515-
@test replace(d, (3=>8)=>(0=>0)) == Dict(1=>2, 0=>0)
516-
@test replace!(d, (3=>8)=>(2=>2)) === d
517-
@test d == Dict(1=>2, 2=>2)
518-
for count = (1, 0x1, big(1))
519-
@test replace(x->x.second == 2, d, 0=>0, count=count) in [Dict(1=>2, 0=>0),
520-
Dict(2=>2, 0=>0)]
521-
end
522-
s = Set([1, 2, 3])
523-
@test replace(x->maybe(2x, x>1), s) == Set([1, 4, 6])
524-
for count = (1, 0x1, big(1))
525-
@test replace(x->maybe(2x, x>1), s, count=count) in [Set([1, 4, 3]), Set([1, 2, 6])]
526-
end
527-
@test replace(s, 1=>4) == Set([2, 3, 4])
528-
@test replace!(s, 1=>2) === s
529-
@test s == Set([2, 3])
530-
@test replace!(x->2x, s, count=0x1) in [Set([4, 3]), Set([2, 6])]
495+
a = [1, 2, 3, 1]
496+
@test replace(x -> iseven(x) ? 2x : x, a) == [1, 4, 3, 1]
497+
@test replace!(x -> iseven(x) ? 2x : x, a) === a
498+
@test a == [1, 4, 3, 1]
499+
@test replace(a, 1=>0) == [0, 4, 3, 0]
500+
for count = (1, 0x1, big(1))
501+
@test replace(a, 1=>0, count=count) == [0, 4, 3, 1]
502+
end
503+
@test replace!(a, 1=>2) === a
504+
@test a == [2, 4, 3, 2]
505+
@test replace!(x->2x, a, count=0x2) == [4, 8, 3, 2]
506+
507+
d = Dict(1=>2, 3=>4)
508+
@test replace(x->x.first > 2, d, 0=>0) == Dict(1=>2, 0=>0)
509+
@test replace!(x -> x.first > 2 ? x.first=>2*x.second : x, d) === d
510+
@test d == Dict(1=>2, 3=>8)
511+
@test replace(d, (3=>8)=>(0=>0)) == Dict(1=>2, 0=>0)
512+
@test replace!(d, (3=>8)=>(2=>2)) === d
513+
@test d == Dict(1=>2, 2=>2)
514+
for count = (1, 0x1, big(1))
515+
@test replace(x->x.second == 2, d, 0=>0, count=count) in [Dict(1=>2, 0=>0),
516+
Dict(2=>2, 0=>0)]
517+
end
518+
s = Set([1, 2, 3])
519+
@test replace(x -> x > 1 ? 2x : x, s) == Set([1, 4, 6])
520+
for count = (1, 0x1, big(1))
521+
@test replace(x -> x > 1 ? 2x : x, s, count=count) in [Set([1, 4, 3]), Set([1, 2, 6])]
522+
end
523+
@test replace(s, 1=>4) == Set([2, 3, 4])
524+
@test replace!(s, 1=>2) === s
525+
@test s == Set([2, 3])
526+
@test replace!(x->2x, s, count=0x1) in [Set([4, 3]), Set([2, 6])]
531527

532-
for count = (0, 0x0, big(0))
533-
@test replace([1, 2], 1=>0, 2=>0, count=count) == [1, 2] # count=0 --> no replacements
534-
end
528+
for count = (0, 0x0, big(0))
529+
@test replace([1, 2], 1=>0, 2=>0, count=count) == [1, 2] # count=0 --> no replacements
535530
end
531+
536532
# test collisions with AbstractSet/AbstractDict
537533
@test replace!(x->2x, Set([3, 6])) == Set([6, 12])
538534
@test replace!(x->2x, Set([1:20;])) == Set([2:2:40;])
539535
@test replace!(kv -> (2kv[1] => kv[2]), Dict(1=>2, 2=>4, 4=>8, 8=>16)) == Dict(2=>2, 4=>4, 8=>8, 16=>16)
540536

541-
# test Some(nothing)
537+
# test nothing & Some(nothing) (should behave as any other value)
542538
a = [1, 2, nothing, 4]
543-
@test replace(x -> x === nothing ? 0 : Some(nothing), a) == [nothing, nothing, 0, nothing]
544-
@test replace(x -> x === nothing ? 0 : nothing, a) == [1, 2, 0, 4]
545-
@test replace!(x -> x !== nothing ? Some(nothing) : nothing, a) == [nothing, nothing, nothing, nothing]
546-
@test replace(iseven, Any[1, 2, 3, 4], nothing) == [1, nothing, 3, nothing]
547-
@test replace(Any[1, 2, 3, 4], 1=>nothing, 3=>nothing) == [nothing, 2, nothing, 4]
548-
s = Set([1, 2, nothing, 4])
549-
@test replace(x -> x === nothing ? 0 : Some(nothing), s) == Set([0, nothing])
550-
@test replace(x -> x === nothing ? 0 : nothing, s) == Set([1, 2, 0, 4])
551-
@test replace(x -> x !== nothing ? Some(nothing) : nothing, s) == Set([nothing])
552-
@test replace(iseven, Set(Any[1, 2, 3, 4]), nothing) == Set([1, nothing, 3, nothing])
553-
@test replace(Set(Any[1, 2, 3, 4]), 1=>nothing, 3=>nothing) == Set([nothing, 2, nothing, 4])
539+
@test replace(x -> x === nothing ? Some(nothing) : nothing, a) == [nothing, nothing, Some(nothing), nothing]
540+
@test replace(x -> x === nothing ? Some(nothing) : nothing, Set(a)) == Set([Some(nothing), nothing])
554541

555542
# avoid recursive call issue #25384
556543
@test_throws MethodError replace!("")

0 commit comments

Comments
 (0)