Skip to content

Commit

Permalink
Merge pull request #18965 from pabloferz/pz/broadcast-ref
Browse files Browse the repository at this point in the history
Make Ref behave as a 0-dimensional array in broadcast
  • Loading branch information
stevengj authored Dec 21, 2016
2 parents a041e6e + 986d575 commit 76a9180
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 7 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ This section lists changes that do not have deprecation warnings.
`broadcast` calls (see above) and produce an `Array`. If you want
a `Range` result, use `+` and `*`, etcetera ([#17623]).

* `broadcast` now treats `Ref` (except for `Ptr`) arguments as 0-dimensional
arrays ([#18965]).

Library improvements
--------------------

Expand Down Expand Up @@ -749,6 +752,7 @@ Language tooling improvements
[#18628]: https://github.com/JuliaLang/julia/issues/18628
[#18839]: https://github.com/JuliaLang/julia/issues/18839
[#18931]: https://github.com/JuliaLang/julia/issues/18931
[#18965]: https://github.com/JuliaLang/julia/issues/18965
[#18977]: https://github.com/JuliaLang/julia/issues/18977
[#19018]: https://github.com/JuliaLang/julia/issues/19018
[#19233]: https://github.com/JuliaLang/julia/issues/19233
Expand Down
22 changes: 16 additions & 6 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ end
# logic for deciding the resulting container type
containertype(x) = containertype(typeof(x))
containertype(::Type) = Any
containertype{T<:Ptr}(::Type{T}) = Any
containertype{T<:Tuple}(::Type{T}) = Tuple
containertype{T<:Ref}(::Type{T}) = Array
containertype{T<:AbstractArray}(::Type{T}) = Array
containertype(ct1, ct2) = promote_containertype(containertype(ct1), containertype(ct2))
@inline containertype(ct1, ct2, cts...) = promote_containertype(containertype(ct1), containertype(ct2, cts...))
Expand All @@ -46,6 +48,7 @@ broadcast_indices(A) = broadcast_indices(containertype(A), A)
broadcast_indices(::Type{Any}, A) = ()
broadcast_indices(::Type{Tuple}, A) = (OneTo(length(A)),)
broadcast_indices(::Type{Array}, A) = indices(A)
broadcast_indices(::Type{Array}, A::Ref) = ()
@inline broadcast_indices(A, B...) = broadcast_shape((), broadcast_indices(A), map(broadcast_indices, B)...)
# shape (i.e., tuple-of-indices) inputs
broadcast_shape(shape::Tuple) = shape
Expand Down Expand Up @@ -118,6 +121,7 @@ map_newindexer(shape, ::Tuple{}) = (), ()
end

Base.@propagate_inbounds _broadcast_getindex(A, I) = _broadcast_getindex(containertype(A), A, I)
Base.@propagate_inbounds _broadcast_getindex(::Type{Array}, A::Ref, I) = A[]
Base.@propagate_inbounds _broadcast_getindex(::Type{Any}, A, I) = A
Base.@propagate_inbounds _broadcast_getindex(::Any, A, I) = A[I]

Expand Down Expand Up @@ -301,15 +305,16 @@ end
"""
broadcast(f, As...)
Broadcasts the arrays, tuples and/or scalars `As` to a container of the
Broadcasts the arrays, tuples, `Ref` and/or scalars `As` to a container of the
appropriate type and dimensions. In this context, anything that is not a
subtype of `AbstractArray` or `Tuple` is considered a scalar. The resulting
container is established by the following rules:
subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple` is considered
a scalar. The resulting container is established by the following rules:
- If all the arguments are scalars, it returns a scalar.
- If the arguments are tuples and zero or more scalars, it returns a tuple.
- If there is at least an array in the arguments, it returns an array
(and treats tuples as 1-dimensional arrays) expanding singleton dimensions.
- If there is at least an array or a `Ref` in the arguments, it returns an array
(and treats any `Ref` as a 0-dimensional array of its contents and any tuple
as a 1-dimensional array) expanding singleton dimensions.
A special syntax exists for broadcasting: `f.(args...)` is equivalent to
`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a
Expand Down Expand Up @@ -351,11 +356,16 @@ julia> abs.((1, -2))
julia> broadcast(+, 1.0, (0, -2.0))
(1.0,-1.0)
julia> broadcast(+, 1.0, (0, -2.0), [1])
julia> broadcast(+, 1.0, (0, -2.0), Ref(1))
2-element Array{Float64,1}:
2.0
0.0
julia> (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1]))
2-element Array{Array{Int64,1},1}:
[1,1]
[2,2]
julia> string.(("one","two","three","four"), ": ", 1:4)
4-element Array{String,1}:
"one: 1"
Expand Down
2 changes: 1 addition & 1 deletion doc/src/manual/arrays.md
Original file line number Diff line number Diff line change
Expand Up @@ -475,7 +475,7 @@ is equivalent to `broadcast(f, args...)`, providing a convenient syntax to broad
[automatically fuse](@ref man-dot-operators) into a single `broadcast` call.

Additionally, [`broadcast()`](@ref) is not limited to arrays (see the function documentation),
it also handles tuples and treats any argument that is not an array or a tuple as a "scalar".
it also handles tuples and treats any argument that is not an array, tuple or `Ref` (except for `Ptr`) as a "scalar".

```julia
julia> convert.(Float32, [1, 2])
Expand Down
7 changes: 7 additions & 0 deletions test/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -366,3 +366,10 @@ let
g() = (a = 1; Base.Broadcast._broadcast_type(x -> x + a, 1.0))
@test @inferred(g()) === Float64
end

# Ref as 0-dimensional array for broadcast
@test (-).(C_NULL, C_NULL)::UInt == 0
@test (+).(1, Ref(2)) == fill(3)
@test (+).(Ref(1), Ref(2)) == fill(3)
@test (+).([[0,2], [1,3]], [1,-1]) == [[1,3], [0,2]]
@test (+).([[0,2], [1,3]], Ref{Vector{Int}}([1,-1])) == [[1,1], [2,2]]

0 comments on commit 76a9180

Please sign in to comment.