Skip to content
This repository has been archived by the owner on Feb 9, 2020. It is now read-only.

Latest commit

 

History

History
151 lines (122 loc) · 3.76 KB

README_0.4.md

File metadata and controls

151 lines (122 loc) · 3.76 KB

Installation

Install this package within Julia using

Pkg.add("FastAnonymous")

Usage

In Julia, you can create an anonymous function this way:

offset = 1.2
f = x->(x+offset)^2

At present, this function is not very efficient, as will be shown below. You can make it more performant by adding the @anon macro:

using FastAnonymous

offset = 1.2
f = @anon x->(x+offset)^2

You can use f like an ordinary function.

Here's a concrete example and speed comparison:

using FastAnonymous

function testn(f, n)
    s = 0.0
    for i = 1:n
        s += f(i/n)
    end
    s
end

function test_inlined(n)
    s = 0.0
    for i = 1:n
        s += (i/n+1.2)^2
    end
    s
end

# Conventional anonymous function
offset = 1.2
f = x->(x+offset)^2
@time testn(f, 1)
julia> @time testn(f, 10^7)
elapsed time: 1.344960759 seconds (610 MB allocated, 4.13% gc time in 28 pauses with 0 full sweep)
2.973333503333424e7

# Hard-wired generic function
sqroffset(x) = (x+1.2)^2
@time testn(sqroffset, 1)
julia> @time testn(sqroffset, 10^7)
elapsed time: 0.627085369 seconds (457 MB allocated, 5.99% gc time in 21 pauses with 0 full sweep)
2.973333503333424e7

# @anon-ized function
g = @anon x->(x+offset)^2
@time testn(g, 1)
julia> @time testn(g, 10^7)
elapsed time: 0.07966527 seconds (112 bytes allocated)
2.973333503333424e7

# Full manual inlining
@time test_inlined(1)
julia> @time test_inlined(10^7)
elapsed time: 0.078703981 seconds (112 bytes allocated)
2.973333503333424e7

You can see that it's more than 20-fold faster than the anonymous-function version, and more than tenfold faster than the generic function version. Indeed, it's as fast as if we had manually inlined this function. Relatedly, it also exhibits no unnecessary memory allocation.

Changing parameter values

With the previous definition of f, the display at the REPL is informative:

julia> f = @anon x->(x+offset)^2
(x) -> quote  # none, line 1:
    Main.^(Main.+(x,offset),2)
end
with:
  offset: 1.2

Main. is a necessary addition for specifying the module scope; without them, you can see the function definition as ^(+(x,offset),2) which is equivalent to (x+offset)^2. At the end, you see the "environment," which consists of stored values, in this case offset: 1.2. After creating f, you can change environmental variables:

julia> f.offset = -7
-7.0

julia> f(7)
0.0

julia> f(9)
4.0

Any symbols that are not arguments end up in environmental variables. As a second example:

julia> x = linspace(0,pi);

julia> f = @anon (A,θ) -> A*sin(x+θ)
(A,θ) -> quote  # none, line 1:
    Main.*(A,Main.sin(Main.+(x,θ)))
end
with:
  x: [0.0,0.0317333,0.0634665,0.0951998,0.126933,0.158666,0.1904,0.222133,0.253866,0.285599    2.85599,2.88773,2.91946,2.95119,2.98293,3.01466,3.04639,3.07813,3.10986,3.14159]

julia> f(10,pi/4)
100-element Array{Float64,1}:
  7.07107
  7.29186
  7.50531
        
 -6.60836
 -6.84316
 -7.07107

julia> f.x[2] = 15
15

julia> f
(A,θ) -> quote  # none, line 1:
    Main.*(A,Main.sin(Main.+(x,θ)))
end
with:
  x: [0.0,15.0,0.0634665,0.0951998,0.126933,0.158666,0.1904,0.222133,0.253866,0.285599    2.85599,2.88773,2.91946,2.95119,2.98293,3.01466,3.04639,3.07813,3.10986,3.14159]

Inner workings

This package uses shameless hacks to implement closures that behave much like a likely native solution. One major difference is that the native closure environment is likely to be immutable, but here it is mutable.

Acknowledgments

This package can be viewed in part as an alternative syntax to the excellent NumericFuns, which was split out from NumericExtensions.