From ab6f431887571a731838dd403b9a68feb4270819 Mon Sep 17 00:00:00 2001 From: Stefan Karpinski Date: Mon, 27 Jun 2016 15:42:08 -0400 Subject: [PATCH] =?UTF-8?q?function=20composition=20and=20negation:=20f?= =?UTF-8?q?=E2=88=98g,=20!f?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base/exports.jl | 1 + base/operators.jl | 45 +++++++++++++++++++++++++++++++++++++++++- doc/src/stdlib/base.md | 1 + test/operators.jl | 10 ++++++++++ 4 files changed, 56 insertions(+), 1 deletion(-) diff --git a/base/exports.jl b/base/exports.jl index 9ea3c205eefc9..a87af1190c7c0 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -233,6 +233,7 @@ export ~, :, =>, + ∘, A_ldiv_B!, A_ldiv_Bc, A_ldiv_Bt, diff --git a/base/operators.jl b/base/operators.jl index 3dc835b4a61b3..1c6a719f59d75 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -665,6 +665,48 @@ julia> [1:5;] |> x->x.^2 |> sum |> inv """ |>(x, f) = f(x) +# function composition + +""" + f ∘ g + +Compose functions: i.e. `(f ∘ g)(args...)` means `f(g(args...))`. The `∘` symbol can be +entered in the Julia REPL (and most editors, appropriately configured) by typing `\circ`. +Example: + +```jldoctest +julia> map(uppercase∘hex, 250:255) +6-element Array{String,1}: + "FA" + "FB" + "FC" + "FD" + "FE" + "FF" +``` +""" +∘(f, g) = (x...)->f(g(x...)) + + +""" + !f::Function + +Predicate function negation: when the argument of `!` is a function, it returns a +function which computes the boolean negation of `f`. Example: + +```jldoctest +julia> str = "∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" +"∀ ε > 0, ∃ δ > 0: |x-y| < δ ⇒ |f(x)-f(y)| < ε" + +julia> filter(isalpha, str) +"εδxyδfxfyε" + +julia> filter(!isalpha, str) +"∀ > 0, ∃ > 0: |-| < ⇒ |()-()| < " +``` +""" +!(f::Function) = (x...)->!f(x...) + # array shape rules promote_shape(::Tuple{}, ::Tuple{}) = () @@ -989,6 +1031,7 @@ export √, ∛, ⊻, + ∘, colon, hcat, vcat, @@ -1002,6 +1045,6 @@ import ..this_module: !, !=, xor, %, ÷, &, *, +, -, /, //, <, <:, <<, <=, ==, >, >=, >>, >>>, <|, |>, \, ^, |, ~, !==, ===, >:, colon, hcat, vcat, hvcat, getindex, setindex!, transpose, ctranspose, - ≥, ≤, ≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛, ⊻ + ≥, ≤, ≠, ⋅, ×, ∈, ∉, ∋, ∌, ⊆, ⊈, ⊊, ∩, ∪, √, ∛, ⊻, ∘ end diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index b95ecc03ae8a7..53c3883a20365 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -122,6 +122,7 @@ Base.method_exists Core.applicable Core.invoke Base.:(|>) +Base.:(∘) ``` ## Syntax diff --git a/test/operators.jl b/test/operators.jl index a649b8addf73b..1a8f885b9daf3 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -79,3 +79,13 @@ Base.:/(::T19714, ::T19714) = T19714() Base.convert(::Type{T19714}, ::Int) = T19714() Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test T19714()/1 === 1/T19714() === T19714() + +# pr #17155 +@testset "function composition" begin + @test (uppercase∘hex)(239487) == "3A77F" +end +@testset "function negation" begin + str = randstring(20) + @test filter(!isupper, str) == replace(str, r"[A-Z]", "") + @test filter(!islower, str) == replace(str, r"[a-z]", "") +end