From ec8adc8b189d4c000fab817db5328ece126f0a85 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Sun, 29 Jan 2017 21:40:21 -0800 Subject: [PATCH 01/11] at-dot macro for adding dots to function calls --- base/broadcast.jl | 62 ++++++++++++++++++++++- base/exports.jl | 1 + doc/src/manual/functions.md | 7 ++- doc/src/manual/mathematical-operations.md | 7 +-- doc/src/manual/performance-tips.md | 7 +-- doc/src/stdlib/arrays.md | 6 ++- src/julia-parser.scm | 4 +- test/broadcast.jl | 35 +++++++++++-- test/subarray.jl | 4 +- 9 files changed, 115 insertions(+), 18 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index 0893f957ac0cb..69855ce223b88 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -7,7 +7,7 @@ using Base: linearindices, tail, OneTo, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, nullable_returntype, null_safe_eltype_op, hasvalue import Base: broadcast, broadcast! -export broadcast_getindex, broadcast_setindex!, dotview +export broadcast_getindex, broadcast_setindex!, dotview, @__DOTS__ typealias ScalarType Union{Type{Any}, Type{Nullable}} @@ -509,4 +509,64 @@ Base.@propagate_inbounds dotview(args...) = getindex(args...) Base.@propagate_inbounds dotview(A::AbstractArray, args...) = view(A, args...) Base.@propagate_inbounds dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) = getindex(A, args...) + +############################################################ +# The parser turns @. into a call to the __DOTS__ macro, +# which converts all function calls and assignments into +# broadcasting "dot" calls/assignments: + +dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) +dottable(x::Symbol) = true +dottable(x::Expr) = x.head != :$ +undot(x) = x +function undot(x::Expr) + if x.head == :.= + Expr(:(=), x.args...) + elseif x.head == :block # occurs in for x=..., y=... + Expr(:block, map(undot, x.args)...) + else + x + end +end +__DOTS__(x) = x +function __DOTS__(x::Expr) + dotargs = map(__DOTS__, x.args) + if x.head == :call && dottable(x.args[1]) + Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) + elseif x.head == :$ + x.args[1] + elseif x.head == :let # don't add dots to "let x=... assignments + Expr(:let, dotargs[1], map(undot, dotargs[2:end])...) + elseif x.head == :for # don't add dots to for x=... assignments + Expr(:for, undot(dotargs[1]), dotargs[2]) + elseif (x.head == :(=) || x.head == :function || x.head == :macro) && + Meta.isexpr(x.args[1], :call) # function or macro definition + Expr(x.head, x.args[1], dotargs[2]) + else + head = string(x.head) + if last(head) == '=' && first(head) != '.' + Expr(Symbol('.',head), dotargs...) + else + Expr(x.head, dotargs...) + end + end +end +""" + @. expr + +Convert every function call or operator in `expr` into a "dot call" +(e.g. convert `f(x)` to `f.(x)`), and convert every assignment in `expr` +to a "dot assignment" (e.g. convert `+=` to `.+=`). + +If you want to *avoid* adding dots for selected function calls in +`expr`, splice those function calls in with `\$`. For example, +`@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))` +(no dot for `sort`). + +(`@.` is equivalent to a call to `@__DOTS__`.) +""" +macro __DOTS__(x) + esc(__DOTS__(x)) +end + end # module diff --git a/base/exports.jl b/base/exports.jl index 736038da9e5cc..9a074342cced1 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1382,6 +1382,7 @@ export @polly, @assert, + @__DOTS__, @enum, @label, @goto, diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 707fea3def36d..8d2813aef77c8 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -642,12 +642,17 @@ overwriting `X` with `sin.(Y)` in-place. If the left-hand side is an array-index e.g. `X[2:end] .= sin.(Y)`, then it translates to `broadcast!` on a `view`, e.g. `broadcast!(sin, view(X, 2:endof(X)), Y)`, so that the left-hand side is updated in-place. +Since adding dots to many operations and function calls in an expression +can be tedious and lead to code that is difficult to read, the macro +[`@.`](@ref @__DOTS__) is provided to convert *every* function call, +operation, and assignment in an expression into the "dotted" version. + ```jldoctest julia> Y = [1.0, 2.0, 3.0, 4.0]; julia> X = similar(Y); # pre-allocate output array -julia> X .= sin.(cos.(Y)) +julia> @. X = sin(cos(Y)) # equivalent to X .= sin.(cos.(Y)) 4-element Array{Float64,1}: 0.514395 -0.404239 diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index ca12d8554af1f..5ea41ab235c90 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -151,13 +151,14 @@ it can combine arrays and scalars, arrays of the same size (performing the operation elementwise), and even arrays of different shapes (e.g. combining row and column vectors to produce a matrix). Moreover, like all vectorized "dot calls," these "dot operators" are -*fusing*. For example, if you compute `2 .* A.^2 .+ sin.(A)` for an -array `A`, it performs a *single* loop over `A`, computing `2a^2 + sin(a)` +*fusing*. For example, if you compute `2 .* A.^2 .+ sin.(A)` (or +equivalently `@. 2A^2 + sin(A)`, using the [`@.`](@ref @__DOTS__) macro) for +an array `A`, it performs a *single* loop over `A`, computing `2a^2 + sin(a)` for each element of `A`. In particular, nested dot calls like `f.(g.(x))` are fused, and "adjacent" binary operators like `x .+ 3 .* x.^2` are equivalent to nested dot calls `(+).(x, (*).(3, (^).(x, 2)))`. -Furthermore, "dotted" updating operators like `a .+= b` are parsed +Furthermore, "dotted" updating operators like `a .+= b` (or `@. a += b`) are parsed as `a .= a .+ b`, where `.=` is a fused *in-place* assignment operation (see the [dot syntax documentation](@ref man-vectorized)). diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 8707fbe7f9702..d22a933237403 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -883,11 +883,12 @@ resulting loops can be fused with surrounding computations. For example, consider the two functions: ```julia -f(x) = 3 * x.^2 + 4 * x + 7 * x.^3 -fdot(x) = 3 .* x.^2 .+ 4 .* x .+ 7 .* x.^3 +f(x) = 3x.^2 + 4x + 7x.^3 +fdot(x) = @. 3x^2 + 4x + 7x^3 # equivalent to 3 .* x.^2 .+ 4 .* x .+ 7 .* x.^3 ``` -Both `f` and `fdot` compute the same thing. However, `fdot` is +Both `f` and `fdot` compute the same thing. However, `fdot` +(defined with the help of the [`@.`](@ref @__DOTS__) macro) is significantly faster when applied to an array: ```julia diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index a5cf36666d85b..cb72a710ba991 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -41,13 +41,15 @@ Base.linspace Base.logspace ``` -## Mathematical operators and functions +## Broadcasting -All mathematical operations and functions are supported for arrays +See also the [dot syntax for vectorizing functions](@ref man-vectorized); +for example, `f.(args...)` implicitly calls `broadcast(f, args...)`. ```@docs Base.broadcast Base.Broadcast.broadcast! +Base.__DOTS__ ``` ## Indexing, Assignment, and Concatenation diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 034ea5d562f84..2b390eeb31eb4 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -2079,7 +2079,9 @@ ((eqv? t #\@) (take-token s) (with-space-sensitive - (let ((head (parse-unary-prefix s))) + (let ((head (if (eq? (peek-token s) '|.|) + (begin (take-token s) '__DOTS__) + (parse-unary-prefix s)))) (if (eq? head '__LINE__) (input-port-line (ts:port s)) (begin diff --git a/test/broadcast.jl b/test/broadcast.jl index 24be1e2db52d7..a1327a47047f1 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -217,7 +217,7 @@ let A = [sqrt(i)+j for i = 1:3, j=1:4] end let x = sin.(1:10) @test atan2.((x->x+1).(x), (x->x+2).(x)) == broadcast(atan2, x+1, x+2) == broadcast(atan2, x.+1, x.+2) - @test sin.(atan2.([x+1,x+2]...)) == sin.(atan2.(x+1,x+2)) + @test sin.(atan2.([x+1,x+2]...)) == sin.(atan2.(x+1,x+2)) == @. sin(atan2(x+1,x+2)) @test sin.(atan2.(x, 3.7)) == broadcast(x -> sin(atan2(x,3.7)), x) @test atan2.(x, 3.7) == broadcast(x -> atan2(x,3.7), x) == broadcast(atan2, x, 3.7) end @@ -226,6 +226,9 @@ let g = Int[] f17300(x) = begin; push!(g, x); x+2; end f17300.(f17300.(f17300.(1:3))) @test g == [1,3,5, 2,4,6, 3,5,7] + empty!(g) + @. f17300(f17300(f17300(1:3))) + @test g == [1,3,5, 2,4,6, 3,5,7] end # fusion with splatted args: let x = sin.(1:10), a = [x] @@ -244,6 +247,28 @@ let x = [1:4;] @test sin.(f17300kw.(x, y=1)) == sin.(f17300kw.(x; y=1)) == sin.(x .+ 1) end +# splice escaping of @. +let x = [4, -9, 1, -16] + @test [2, 3, 4, 5] == @.(1 + sqrt($sort(abs(x)))) +end + +# interaction of @. with let +@test [1,4,9] == @. let x = [1,2,3]; x^2; end + +# interaction of @. with for loops +let x = [1,2,3], y = x + @. for i = 1:3 + y = y^2 # should convert to y .= y.^2 + end + @test x == [1,256,6561] +end + +# interaction of @. with function definitions +let x = [1,2,3] + @. f(x) = x^2 + @test f(x) == [1,4,9] +end + # PR #17510: Fused in-place assignment let x = [1:4;], y = x y .= 2:5 @@ -259,15 +284,15 @@ let x = [1:4;], y = x @test y === x == [9,9,9,9] y .-= 1 @test y === x == [8,8,8,8] - y .-= 1:4 + @. y -= 1:4 @test y === x == [7,6,5,4] x[1:2] .= 1 @test y === x == [1,1,5,4] - x[1:2] .+= [2,3] + @. x[1:2] .+= [2,3] @test y === x == [3,4,5,4] - x[:] .= 0 + @. x[:] .= 0 @test y === x == [0,0,0,0] - x[2:end] .= 1:3 + @. x[2:end] = 1:3 @test y === x == [0,1,2,3] end let a = [[4, 5], [6, 7]] diff --git a/test/subarray.jl b/test/subarray.jl index b6eeee5181fe8..daf056e5d3be8 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -479,7 +479,7 @@ Y = 4:-1:1 @test isa(@view(X[1:3]), SubArray) -@test X[1:end] == @view X[1:end] +@test X[1:end] == @. @view X[1:end] @test X[1:end-3] == @view X[1:end-3] @test X[1:end,2,2] == @view X[1:end,2,2] # @test X[1,1:end-2] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation @@ -518,7 +518,7 @@ end @test x == [5,6,35,4] x[Y[2:3]] .= 7:8 @test x == [5,8,7,4] - x[(3,)..., ()...] .+= 3 + @. x[(3,)..., ()...] += 3 @test x == [5,8,10,4] i = Int[] # test that lhs expressions in update operations are evaluated only once: From efa588b0dc7223d5312a5c25ada9c1aabd4f0888 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Sun, 29 Jan 2017 22:44:59 -0800 Subject: [PATCH 02/11] NEWS for at-dots --- NEWS.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/NEWS.md b/NEWS.md index b0e5489b8fd7a..67ece9f1da05e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,6 +41,9 @@ Language changes * `isa` is now parsed as an infix operator with the same precedence as `in` ([#19677]). + * `@.` is now parsed as `@__DOTS__`, and can be used to add dots to + every function call, operator, and assignment in an expression ([#20321]). + Breaking changes ---------------- From 5522239555cb2fb4f3c130e49e2854ed368671b7 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 30 Jan 2017 16:18:27 -0800 Subject: [PATCH 03/11] fix doc ref to __DOTS__ --- doc/src/stdlib/arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index cb72a710ba991..6ecae0ad48e5c 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -49,7 +49,7 @@ for example, `f.(args...)` implicitly calls `broadcast(f, args...)`. ```@docs Base.broadcast Base.Broadcast.broadcast! -Base.__DOTS__ +Base.@__DOTS__ ``` ## Indexing, Assignment, and Concatenation From 658a3714aa91a7ded551bcfc92979fb3652ef839 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 30 Jan 2017 20:40:24 -0800 Subject: [PATCH 04/11] doc tweak: 'Broadcasting' was not a unique title, expand the section intro somewhat --- doc/src/stdlib/arrays.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 6ecae0ad48e5c..1a7f55407b183 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -41,15 +41,19 @@ Base.linspace Base.logspace ``` -## Broadcasting +## Broadcast and vectorization See also the [dot syntax for vectorizing functions](@ref man-vectorized); for example, `f.(args...)` implicitly calls `broadcast(f, args...)`. +Rather than relying on "vectorized" methods of function like `sin` +to operate on arrays, you should use `sin.(a)` to vectorize via `broadcast`. ```@docs Base.broadcast Base.Broadcast.broadcast! Base.@__DOTS__ +Base.Broadcast.broadcast_getindex +Base.Broadcast.broadcast_setindex! ``` ## Indexing, Assignment, and Concatenation @@ -65,8 +69,6 @@ Base.parent Base.parentindexes Base.slicedim Base.setindex!(::AbstractArray, ::Any, ::Any...) -Base.Broadcast.broadcast_getindex -Base.Broadcast.broadcast_setindex! Base.isassigned Base.cat Base.vcat From c693ac1859e088d7fdbb844e50d5b0fd01bfa696 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 30 Jan 2017 20:41:18 -0800 Subject: [PATCH 05/11] __DOTS__ -> __DOT__ --- NEWS.md | 2 +- base/broadcast.jl | 16 ++++++++-------- base/exports.jl | 2 +- doc/src/manual/functions.md | 2 +- doc/src/manual/mathematical-operations.md | 2 +- doc/src/manual/performance-tips.md | 2 +- doc/src/stdlib/arrays.md | 2 +- src/julia-parser.scm | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index 67ece9f1da05e..bbbb62c25db86 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,7 +41,7 @@ Language changes * `isa` is now parsed as an infix operator with the same precedence as `in` ([#19677]). - * `@.` is now parsed as `@__DOTS__`, and can be used to add dots to + * `@.` is now parsed as `@__DOT__`, and can be used to add dots to every function call, operator, and assignment in an expression ([#20321]). Breaking changes diff --git a/base/broadcast.jl b/base/broadcast.jl index 69855ce223b88..0bcedf115166c 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -7,7 +7,7 @@ using Base: linearindices, tail, OneTo, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, nullable_returntype, null_safe_eltype_op, hasvalue import Base: broadcast, broadcast! -export broadcast_getindex, broadcast_setindex!, dotview, @__DOTS__ +export broadcast_getindex, broadcast_setindex!, dotview, @__DOT__ typealias ScalarType Union{Type{Any}, Type{Nullable}} @@ -511,7 +511,7 @@ Base.@propagate_inbounds dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) ############################################################ -# The parser turns @. into a call to the __DOTS__ macro, +# The parser turns @. into a call to the __DOT__ macro, # which converts all function calls and assignments into # broadcasting "dot" calls/assignments: @@ -528,9 +528,9 @@ function undot(x::Expr) x end end -__DOTS__(x) = x -function __DOTS__(x::Expr) - dotargs = map(__DOTS__, x.args) +__DOT__(x) = x +function __DOT__(x::Expr) + dotargs = map(__DOT__, x.args) if x.head == :call && dottable(x.args[1]) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) elseif x.head == :$ @@ -563,10 +563,10 @@ If you want to *avoid* adding dots for selected function calls in `@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))` (no dot for `sort`). -(`@.` is equivalent to a call to `@__DOTS__`.) +(`@.` is equivalent to a call to `@__DOT__`.) """ -macro __DOTS__(x) - esc(__DOTS__(x)) +macro __DOT__(x) + esc(__DOT__(x)) end end # module diff --git a/base/exports.jl b/base/exports.jl index 9a074342cced1..d1a48a60a6235 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1382,7 +1382,7 @@ export @polly, @assert, - @__DOTS__, + @__DOT__, @enum, @label, @goto, diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 8d2813aef77c8..40f550a7029a1 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -644,7 +644,7 @@ so that the left-hand side is updated in-place. Since adding dots to many operations and function calls in an expression can be tedious and lead to code that is difficult to read, the macro -[`@.`](@ref @__DOTS__) is provided to convert *every* function call, +[`@.`](@ref @__DOT__) is provided to convert *every* function call, operation, and assignment in an expression into the "dotted" version. ```jldoctest diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index 5ea41ab235c90..cc49e9f401769 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -152,7 +152,7 @@ the operation elementwise), and even arrays of different shapes (e.g. combining row and column vectors to produce a matrix). Moreover, like all vectorized "dot calls," these "dot operators" are *fusing*. For example, if you compute `2 .* A.^2 .+ sin.(A)` (or -equivalently `@. 2A^2 + sin(A)`, using the [`@.`](@ref @__DOTS__) macro) for +equivalently `@. 2A^2 + sin(A)`, using the [`@.`](@ref @__DOT__) macro) for an array `A`, it performs a *single* loop over `A`, computing `2a^2 + sin(a)` for each element of `A`. In particular, nested dot calls like `f.(g.(x))` are fused, and "adjacent" binary operators like `x .+ 3 .* x.^2` are diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index d22a933237403..96a01635eeabd 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -888,7 +888,7 @@ fdot(x) = @. 3x^2 + 4x + 7x^3 # equivalent to 3 .* x.^2 .+ 4 .* x .+ 7 .* x.^3 ``` Both `f` and `fdot` compute the same thing. However, `fdot` -(defined with the help of the [`@.`](@ref @__DOTS__) macro) is +(defined with the help of the [`@.`](@ref @__DOT__) macro) is significantly faster when applied to an array: ```julia diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 1a7f55407b183..00691c977fdb9 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -51,7 +51,7 @@ to operate on arrays, you should use `sin.(a)` to vectorize via `broadcast`. ```@docs Base.broadcast Base.Broadcast.broadcast! -Base.@__DOTS__ +Base.@__DOT__ Base.Broadcast.broadcast_getindex Base.Broadcast.broadcast_setindex! ``` diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 2b390eeb31eb4..27a6ed2915e01 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -2080,7 +2080,7 @@ (take-token s) (with-space-sensitive (let ((head (if (eq? (peek-token s) '|.|) - (begin (take-token s) '__DOTS__) + (begin (take-token s) '__DOT__) (parse-unary-prefix s)))) (if (eq? head '__LINE__) (input-port-line (ts:port s)) From 88b9d4e3cc6e7b3342b56812ec607cccc38575b4 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 30 Jan 2017 21:46:37 -0800 Subject: [PATCH 06/11] __DOT__ -> __dot__ --- NEWS.md | 2 +- base/broadcast.jl | 16 ++++++++-------- base/exports.jl | 2 +- doc/src/manual/functions.md | 2 +- doc/src/manual/mathematical-operations.md | 2 +- doc/src/manual/performance-tips.md | 2 +- doc/src/stdlib/arrays.md | 2 +- src/julia-parser.scm | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/NEWS.md b/NEWS.md index bbbb62c25db86..b6bda842d4c46 100644 --- a/NEWS.md +++ b/NEWS.md @@ -41,7 +41,7 @@ Language changes * `isa` is now parsed as an infix operator with the same precedence as `in` ([#19677]). - * `@.` is now parsed as `@__DOT__`, and can be used to add dots to + * `@.` is now parsed as `@__dot__`, and can be used to add dots to every function call, operator, and assignment in an expression ([#20321]). Breaking changes diff --git a/base/broadcast.jl b/base/broadcast.jl index 0bcedf115166c..df787e354d546 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -7,7 +7,7 @@ using Base: linearindices, tail, OneTo, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, nullable_returntype, null_safe_eltype_op, hasvalue import Base: broadcast, broadcast! -export broadcast_getindex, broadcast_setindex!, dotview, @__DOT__ +export broadcast_getindex, broadcast_setindex!, dotview, @__dot__ typealias ScalarType Union{Type{Any}, Type{Nullable}} @@ -511,7 +511,7 @@ Base.@propagate_inbounds dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) ############################################################ -# The parser turns @. into a call to the __DOT__ macro, +# The parser turns @. into a call to the __dot__ macro, # which converts all function calls and assignments into # broadcasting "dot" calls/assignments: @@ -528,9 +528,9 @@ function undot(x::Expr) x end end -__DOT__(x) = x -function __DOT__(x::Expr) - dotargs = map(__DOT__, x.args) +__dot__(x) = x +function __dot__(x::Expr) + dotargs = map(__dot__, x.args) if x.head == :call && dottable(x.args[1]) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) elseif x.head == :$ @@ -563,10 +563,10 @@ If you want to *avoid* adding dots for selected function calls in `@. sqrt(abs(\$sort(x)))` is equivalent to `sqrt.(abs.(sort(x)))` (no dot for `sort`). -(`@.` is equivalent to a call to `@__DOT__`.) +(`@.` is equivalent to a call to `@__dot__`.) """ -macro __DOT__(x) - esc(__DOT__(x)) +macro __dot__(x) + esc(__dot__(x)) end end # module diff --git a/base/exports.jl b/base/exports.jl index d1a48a60a6235..0ab2d7c70bd81 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1382,7 +1382,7 @@ export @polly, @assert, - @__DOT__, + @__dot__, @enum, @label, @goto, diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 40f550a7029a1..004c2d14340b6 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -644,7 +644,7 @@ so that the left-hand side is updated in-place. Since adding dots to many operations and function calls in an expression can be tedious and lead to code that is difficult to read, the macro -[`@.`](@ref @__DOT__) is provided to convert *every* function call, +[`@.`](@ref @__dot__) is provided to convert *every* function call, operation, and assignment in an expression into the "dotted" version. ```jldoctest diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index cc49e9f401769..580664a24d710 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -152,7 +152,7 @@ the operation elementwise), and even arrays of different shapes (e.g. combining row and column vectors to produce a matrix). Moreover, like all vectorized "dot calls," these "dot operators" are *fusing*. For example, if you compute `2 .* A.^2 .+ sin.(A)` (or -equivalently `@. 2A^2 + sin(A)`, using the [`@.`](@ref @__DOT__) macro) for +equivalently `@. 2A^2 + sin(A)`, using the [`@.`](@ref @__dot__) macro) for an array `A`, it performs a *single* loop over `A`, computing `2a^2 + sin(a)` for each element of `A`. In particular, nested dot calls like `f.(g.(x))` are fused, and "adjacent" binary operators like `x .+ 3 .* x.^2` are diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 96a01635eeabd..2dae523e568ad 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -888,7 +888,7 @@ fdot(x) = @. 3x^2 + 4x + 7x^3 # equivalent to 3 .* x.^2 .+ 4 .* x .+ 7 .* x.^3 ``` Both `f` and `fdot` compute the same thing. However, `fdot` -(defined with the help of the [`@.`](@ref @__DOT__) macro) is +(defined with the help of the [`@.`](@ref @__dot__) macro) is significantly faster when applied to an array: ```julia diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 00691c977fdb9..253fb1f512ef2 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -51,7 +51,7 @@ to operate on arrays, you should use `sin.(a)` to vectorize via `broadcast`. ```@docs Base.broadcast Base.Broadcast.broadcast! -Base.@__DOT__ +Base.@__dot__ Base.Broadcast.broadcast_getindex Base.Broadcast.broadcast_setindex! ``` diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 27a6ed2915e01..df351deb5171f 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -2080,7 +2080,7 @@ (take-token s) (with-space-sensitive (let ((head (if (eq? (peek-token s) '|.|) - (begin (take-token s) '__DOT__) + (begin (take-token s) '__dot__) (parse-unary-prefix s)))) (if (eq? head '__LINE__) (input-port-line (ts:port s)) From 370920e3d710b4f3a9bc479652f5cca5b8709ec1 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Mon, 30 Jan 2017 22:02:48 -0800 Subject: [PATCH 07/11] don't add redundant dots in at-dot --- base/broadcast.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index df787e354d546..c86ed6eb565ee 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -5,7 +5,7 @@ module Broadcast using Base.Cartesian using Base: linearindices, tail, OneTo, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, - nullable_returntype, null_safe_eltype_op, hasvalue + nullable_returntype, null_safe_eltype_op, hasvalue, isoperator import Base: broadcast, broadcast! export broadcast_getindex, broadcast_setindex!, dotview, @__dot__ @@ -516,7 +516,7 @@ Base.@propagate_inbounds dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) # broadcasting "dot" calls/assignments: dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) -dottable(x::Symbol) = true +dottable(x::Symbol) = !isoperator(x) || first(string(x)) != '.' # don't add dots to dot operators dottable(x::Expr) = x.head != :$ undot(x) = x function undot(x::Expr) From 29bb4320630605de26e58589b3598af67c7ed459 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 31 Jan 2017 07:28:57 -0800 Subject: [PATCH 08/11] do add dots to .. operator in at-dot --- base/broadcast.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/broadcast.jl b/base/broadcast.jl index c86ed6eb565ee..249c16fd5fe27 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -516,7 +516,7 @@ Base.@propagate_inbounds dotview{T<:AbstractArray}(A::AbstractArray{T}, args...) # broadcasting "dot" calls/assignments: dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) -dottable(x::Symbol) = !isoperator(x) || first(string(x)) != '.' # don't add dots to dot operators +dottable(x::Symbol) = !isoperator(x) || first(string(x)) != '.' || x == :.. # don't add dots to dot operators dottable(x::Expr) = x.head != :$ undot(x) = x function undot(x::Expr) From e81d3ecfd6a0a800fc517e7ff05c32837a890267 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 31 Jan 2017 14:19:33 -0800 Subject: [PATCH 09/11] typo --- doc/src/stdlib/arrays.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/stdlib/arrays.md b/doc/src/stdlib/arrays.md index 253fb1f512ef2..47bb3663fd2de 100644 --- a/doc/src/stdlib/arrays.md +++ b/doc/src/stdlib/arrays.md @@ -45,7 +45,7 @@ Base.logspace See also the [dot syntax for vectorizing functions](@ref man-vectorized); for example, `f.(args...)` implicitly calls `broadcast(f, args...)`. -Rather than relying on "vectorized" methods of function like `sin` +Rather than relying on "vectorized" methods of functions like `sin` to operate on arrays, you should use `sin.(a)` to vectorize via `broadcast`. ```@docs From 2286a66bd4a8f152fb642045f60d65954cf40def Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Tue, 31 Jan 2017 14:23:55 -0800 Subject: [PATCH 10/11] comments on at-dot tests --- test/broadcast.jl | 8 ++++---- test/subarray.jl | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/broadcast.jl b/test/broadcast.jl index a1327a47047f1..83f74b7fe99dc 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -284,15 +284,15 @@ let x = [1:4;], y = x @test y === x == [9,9,9,9] y .-= 1 @test y === x == [8,8,8,8] - @. y -= 1:4 + @. y -= 1:4 # @. should convert to .-= @test y === x == [7,6,5,4] x[1:2] .= 1 @test y === x == [1,1,5,4] - @. x[1:2] .+= [2,3] + @. x[1:2] .+= [2,3] # use .+= to make sure @. works with dotted assignment @test y === x == [3,4,5,4] - @. x[:] .= 0 + @. x[:] .= 0 # use .= to make sure @. works with dotted assignment @test y === x == [0,0,0,0] - @. x[2:end] = 1:3 + @. x[2:end] = 1:3 # @. should convert to .= @test y === x == [0,1,2,3] end let a = [[4, 5], [6, 7]] diff --git a/test/subarray.jl b/test/subarray.jl index daf056e5d3be8..ee5a703768d28 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -479,7 +479,7 @@ Y = 4:-1:1 @test isa(@view(X[1:3]), SubArray) -@test X[1:end] == @. @view X[1:end] +@test X[1:end] == @.(@view X[1:end]) == @view(@. X[1:end]) # test compatibility of @. and @view @test X[1:end-3] == @view X[1:end-3] @test X[1:end,2,2] == @view X[1:end,2,2] # @test X[1,1:end-2] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation @@ -518,7 +518,7 @@ end @test x == [5,6,35,4] x[Y[2:3]] .= 7:8 @test x == [5,8,7,4] - @. x[(3,)..., ()...] += 3 + @. x[(3,)..., ()...] += 3 # @. should convert to .+=, test compatibility with @views @test x == [5,8,10,4] i = Int[] # test that lhs expressions in update operations are evaluated only once: From b0770f6267988f395eaa5c1de42c3b87466c259b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Wed, 1 Feb 2017 15:59:44 -0500 Subject: [PATCH 11/11] test tweaks --- test/subarray.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/subarray.jl b/test/subarray.jl index ee5a703768d28..cb10e3f1c4be4 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -479,7 +479,7 @@ Y = 4:-1:1 @test isa(@view(X[1:3]), SubArray) -@test X[1:end] == @.(@view X[1:end]) == @view(@. X[1:end]) # test compatibility of @. and @view +@test X[1:end] == @.(@view X[1:end]) # test compatibility of @. and @view @test X[1:end-3] == @view X[1:end-3] @test X[1:end,2,2] == @view X[1:end,2,2] # @test X[1,1:end-2] == @view X[1,1:end-2] # TODO: Re-enable after partial linear indexing deprecation @@ -526,6 +526,8 @@ end @test x == [5,8,10,9] && i == [4] x[push!(i,3)[end]] += 2 @test x == [5,8,12,9] && i == [4,3] + @. x[3:end] = 0 # make sure @. works with end expressions in @views + @test x == [5,8,0,0] end @views @test isa(X[1:3], SubArray) @test X[1:end] == @views X[1:end]