Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Julia: adding anonymous functions together

If I define some anonymous functions a(x) and b(x) as

a = x -> x^2
b = x -> 2x

it would be helpful for recursive problems to add them together, say over the duration of some loop:

for i=1:5
    a = x -> a(x) + b(x)
end

where the goal would be to have this represented internally each loop iteration as

a = x -> x^2 + 2x
a = x -> x^2 + 2x + x^2 + 2x
a = x -> x^2 + 2x + x^2 + 2x + x^2 + 2x
...

But, this fails and returns some error. I'm assuming it's because calling the new a(x) is interpreted as: a(2) = 2 -> x^2 + x^2 + ... + x^2 + 2x

julia> a(2)
ERROR: StackOverflowError:
 in (::##35#36)(::Int64) at ./REPL[115]:0
 in (::##35#36)(::Int64) at ./REPL[115]:1 (repeats 26666 times)

Is there any way around this?

like image 267
Daniel R. Livingston Avatar asked Jun 14 '17 16:06

Daniel R. Livingston


2 Answers

You can do exactly what you're looking for using the let keyword:

a = x -> x^2
b = x -> 2x

for i=1:5
  a = let a = a; x -> a(x) + b(x); end
end

a(2) # returns 24

Explanation

The let keyword allows you to create a block with local scope, and return the last statement in the block back to its caller scope. (contrast that with the begin keyword for instance, which does not introduce new scope).

If you pass a sequence of "assignments" to the let keyword, these become variables local to the block (allowing you, therefore, to re-use variable names that already exist in your workspace). The declaration let a = a is perfectly valid and means "create a local variable a which is initialised from the a variable of the outer scope" --- though if we wanted to be really clear, we could have written it like this instead:

for i=1:5
  a = let a_old = a
        x -> a_old(x) + b(x); 
      end
end


then again, if you were willing to use an a_old variable, you could have just done this instead:
for i=1:5; a_old = a; a = x-> a_old(x) + b(x); end

let is a very useful keyword: it's extremely handy for creating on-the-spot closures; in fact, this is exactly what we did here: we have returned a closure, where the "local variable a" essentially became a closed variable.


PS. Since matlab was mentioned, what you're doing when you evaluate a = @ (x) a(x) + b(x) in matlab is essentially creating a closure. In matlab you can inspect all the closed variables (i.e. the 'workspace' of the closure) using the functions command

PPS. The Dr Livingstone, I presume?

like image 161
Tasos Papastylianou Avatar answered Sep 30 '22 03:09

Tasos Papastylianou


Using Polynomials package could be a way. This would go:

julia> using Polynomials   # install with Pkg.add("Polynomials")

julia> x = Poly([0,1])
Poly(x)

julia> a = x^2
Poly(x^2)

julia> b = 2x
Poly(2*x)

julia> a = a+b
Poly(2*x + x^2)

julia> a(2.0)
8.0

The reason this works is because essentially the behavior you want is symbolic manipulation of functions. Julia does not work this way (it's a compiler - or ahead-of-time (AOT) compiler), but it is flexible. If fancier functions than polynomials are required, maybe a symbolic math package would help (there is SymPy, but I haven't used it).

like image 44
Dan Getz Avatar answered Sep 30 '22 03:09

Dan Getz