From 9a95a9d18a32e75308c1032cdef5ce379f4e0840 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 26 Jan 2017 11:09:02 -0500 Subject: [PATCH 1/3] support dots for unary operators (closes #14161) --- src/julia-parser.scm | 13 +++++++------ test/broadcast.jl | 5 +++++ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/julia-parser.scm b/src/julia-parser.scm index fece65ee370d2..5d8de07bec78f 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -65,10 +65,11 @@ t)) (define (operator-precedence op) (get prec-table op 0)) -(define unary-ops '(+ - ! ¬ ~ |<:| |>:| √ ∛ ∜)) +(define unary-ops (append! '(|<:| |>:| ~) + (add-dots '(+ - ! ¬ √ ∛ ∜)))) ; operators that are both unary and binary -(define unary-and-binary-ops '(+ - $ & ~)) +(define unary-and-binary-ops '(+ - $ & ~ |.+| |.-|)) ; operators that are special forms, not function names (define syntactic-operators @@ -93,9 +94,9 @@ (define operators (filter (lambda (x) (not (is-word-operator? x))) - (list* '~ '! '¬ '-> '√ '∛ '∜ ctrans-op trans-op vararg-op - (delete-duplicates - (apply append (map eval prec-names)))))) + (delete-duplicates + (list* '-> ctrans-op trans-op vararg-op + (append unary-ops (apply append (map eval prec-names))))))) (define op-chars (delete-duplicates @@ -200,7 +201,7 @@ (loop newop (peek-char port))) str)) str)))) - (if (or (equal? str "--") (equal? str ".!")) + (if (equal? str "--") (error (string "invalid operator \"" str "\""))) (string->symbol str)))) diff --git a/test/broadcast.jl b/test/broadcast.jl index 24be1e2db52d7..cd03215823b4a 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -302,6 +302,11 @@ let g = Int[], ⊕ = (a,b) -> let c=a+2b; push!(g, c); c; end @test g == [21,221,24,424,27,627] # test for loop fusion end +# Fused unary operators +@test .√[3,4,5] == sqrt.([3,4,5]) +@test .![true, true, false] == [false, false, true] +@test .-[1,2,3] == -[1,2,3] == .+[-1,-2,-3] == [-1,-2,-3] + # PR 16988 @test Base.promote_op(+, Bool) === Int @test isa(broadcast(+, [true]), Array{Int,1}) From b15811a589688faae495af69259d075902319ed1 Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 26 Jan 2017 11:14:23 -0500 Subject: [PATCH 2/3] docs for unary dot operators --- NEWS.md | 1 + doc/src/manual/functions.md | 2 +- doc/src/manual/mathematical-operations.md | 4 +++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/NEWS.md b/NEWS.md index c7ec4a3cd5d31..123bb0bca659d 100644 --- a/NEWS.md +++ b/NEWS.md @@ -34,6 +34,7 @@ Language changes the `broadcast` call `(⨳).(a, b)`. Hence, one no longer defines methods for `.*` etcetera. This also means that "dot operations" automatically fuse into a single loop, along with other dot calls `f.(x)`. ([#17623]) + Similarly for unary operators ([#20249]). * Newly defined methods are no longer callable from the same dynamic runtime scope they were defined in ([#17057]). diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 707fea3def36d..e29e10c98afb6 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -655,7 +655,7 @@ julia> X .= sin.(cos.(Y)) -0.608083 ``` -Operators like `.*` are handled with the same mechanism: +Binary (or unary) operators like `.+` are handled with the same mechanism: they are equivalent to `broadcast` calls and are fused with other nested "dot" calls. `X .+= Y` etcetera is equivalent to `X .= X .+ Y` and results in a fused in-place assignment; see also [dot operators](@ref man-dot-operators). diff --git a/doc/src/manual/mathematical-operations.md b/doc/src/manual/mathematical-operations.md index e62b99b6408c1..07fa4948d653c 100644 --- a/doc/src/manual/mathematical-operations.md +++ b/doc/src/manual/mathematical-operations.md @@ -135,7 +135,9 @@ to perform `^` element-by-element on arrays. For example, `[1,2,3] ^ 3` is not defined, since there is no standard mathematical meaning to "cubing" an array, but `[1,2,3] .^ 3` is defined as computing the elementwise -(or "vectorized") result `[1^3, 2^3, 3^3]`. +(or "vectorized") result `[1^3, 2^3, 3^3]`. Similarly for unary +operators like `!` or `√`, there is a corresponding `.√` that +applies the operator elementwise. More specifically, `a .^ b` is parsed as the ["dot" call](@ref man-vectorized) `(^).(a,b)`, which performs a [broadcast](@ref Broadcasting) operation: From 144624edb145611fcd0205b0c0759181ad5dd63b Mon Sep 17 00:00:00 2001 From: "Steven G. Johnson" Date: Thu, 26 Jan 2017 13:42:42 -0500 Subject: [PATCH 3/3] work around #20249 --- test/nullable.jl | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/nullable.jl b/test/nullable.jl index 025522312427f..cf103f2e203e6 100644 --- a/test/nullable.jl +++ b/test/nullable.jl @@ -467,10 +467,10 @@ sqr(x) = x^2 @test Nullable(2) .^ Nullable{Int}() |> isnull_oftype(Int) # multi-arg broadcast -@test Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ - Nullable(1) === Nullable(6) -@test Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable{Int}() .+ - Nullable(1) .+ Nullable(1) |> isnull_oftype(Int) +@test (Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ + Nullable(1) === Nullable(6)) +@test (Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable{Int}() .+ + Nullable(1) .+ Nullable(1) |> isnull_oftype(Int)) # these are not inferrable because there are too many arguments us = map(Nullable, 1:20)