diff --git a/NEWS.md b/NEWS.md index b5167691db82a..b96a616c17854 100644 --- a/NEWS.md +++ b/NEWS.md @@ -28,6 +28,9 @@ This section lists changes that do not have deprecation warnings. for `real(z) < 0`, which differs from `log(gamma(z))` by multiples of 2π in the imaginary part ([#18330]). + * `broadcast` now handles tuples, and treats any argument that is not a tuple + or an array as a "scalar" ([#16986]). + Library improvements -------------------- @@ -638,6 +641,7 @@ Language tooling improvements [#16854]: https://github.com/JuliaLang/julia/issues/16854 [#16953]: https://github.com/JuliaLang/julia/issues/16953 [#16972]: https://github.com/JuliaLang/julia/issues/16972 +[#16986]: https://github.com/JuliaLang/julia/issues/16986 [#17033]: https://github.com/JuliaLang/julia/issues/17033 [#17037]: https://github.com/JuliaLang/julia/issues/17037 [#17075]: https://github.com/JuliaLang/julia/issues/17075 diff --git a/base/broadcast.jl b/base/broadcast.jl index 890780a00f12e..d6801a7e074b4 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -271,8 +271,73 @@ end @inline broadcast_c(f, ::Type{Any}, a...) = f(a...) @inline broadcast_c(f, ::Type{Array}, As...) = broadcast_t(f, promote_eltype_op(f, As...), As...) -@inline broadcast(f, As...) = broadcast_c(f, containertype(As...), As...) +""" + broadcast(f, As...) + +Broadcasts the arrays, tuples 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 stablished 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. + +A special syntax exists for broadcasting: `f.(args...)` is equivalent to +`broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a +single broadcast loop. + +```jldoctest +julia> A = [1, 2, 3, 4, 5] +5-element Array{Int64,1}: + 1 + 2 + 3 + 4 + 5 + +julia> B = [1 2; 3 4; 5 6; 7 8; 9 10] +5×2 Array{Int64,2}: + 1 2 + 3 4 + 5 6 + 7 8 + 9 10 + +julia> broadcast(+, A, B) +5×2 Array{Int64,2}: + 2 3 + 5 6 + 8 9 + 11 12 + 14 15 +julia> parse.(Int, ["1", "2"]) +2-element Array{Int64,1}: + 1 + 2 + +julia> abs.((1, -2)) +(1,2) + +julia> broadcast(+, 1.0, (0, -2.0)) +(1.0,-1.0) + +julia> broadcast(+, 1.0, (0, -2.0), [1]) +2-element Array{Float64,1}: + 2.0 + 0.0 + +julia> string.(("one","two","three","four"), ": ", 1:4) +4-element Array{String,1}: + "one: 1" + "two: 2" + "three: 3" + "four: 4" +``` +""" +@inline broadcast(f, As...) = broadcast_c(f, containertype(As...), As...) """ bitbroadcast(f, As...) diff --git a/doc/manual/arrays.rst b/doc/manual/arrays.rst index 93b09ab1525c3..ba99ceb041973 100644 --- a/doc/manual/arrays.rst +++ b/doc/manual/arrays.rst @@ -526,6 +526,27 @@ function elementwise: Elementwise operators such as ``.+`` and ``.*`` perform broadcasting if necessary. There is also a :func:`broadcast!` function to specify an explicit destination, and :func:`broadcast_getindex` and :func:`broadcast_setindex!` that broadcast the indices before indexing. Moreover, ``f.(args...)`` is equivalent to ``broadcast(f, args...)``, providing a convenient syntax to broadcast any function (:ref:`man-dot-vectorizing`). +Additionally, :func:`broadcast` 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". + +.. doctest:: + + julia> convert.(Float32, [1, 2]) + 2-element Array{Float32,1}: + 1.0 + 2.0 + + julia> ceil.((UInt8,), [1.2 3.4; 5.6 6.7]) + 2×2 Array{UInt8,2}: + 0x02 0x04 + 0x06 0x07 + + julia> string.(1:3, ". ", ["First", "Second", "Third"]) + 3-element Array{String,1}: + "1. First" + "2. Second" + "3. Third" + + Implementation -------------- diff --git a/doc/stdlib/arrays.rst b/doc/stdlib/arrays.rst index 7507f955b5beb..bbc21b547cb5b 100644 --- a/doc/stdlib/arrays.rst +++ b/doc/stdlib/arrays.rst @@ -597,7 +597,13 @@ All mathematical operations and functions are supported for arrays .. Docstring generated from Julia source - Broadcasts the arrays ``As`` to a common size by expanding singleton dimensions, and returns an array of the results ``f(as...)`` for each position. + Broadcasts the arrays, tuples 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 stablished 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. + + A special syntax exists for broadcasting: ``f.(args...)`` is equivalent to ``broadcast(f, args...)``\ , and nested ``f.(g.(args...))`` calls are fused into a single broadcast loop. .. doctest:: @@ -625,6 +631,29 @@ All mathematical operations and functions are supported for arrays 11 12 14 15 + julia> parse.(Int, ["1", "2"]) + 2-element Array{Int64,1}: + 1 + 2 + + julia> abs.((1, -2)) + (1,2) + + julia> broadcast(+, 1.0, (0, -2.0)) + (1.0,-1.0) + + julia> broadcast(+, 1.0, (0, -2.0), [1]) + 2-element Array{Float64,1}: + 2.0 + 0.0 + + julia> string.(("one","two","three","four"), ": ", 1:4) + 4-element Array{String,1}: + "one: 1" + "two: 2" + "three: 3" + "four: 4" + .. function:: broadcast!(f, dest, As...) .. Docstring generated from Julia source