thanks @GunnarFarneback. Inspired by your suggestion and other searches of “local evaluation”, I come up with the following not-so-perfect solution that only works for very simple expressions:
function expr2fun(ex)
vars = getvars(ex)
args = Expr(:tuple, vars...)
return eval(:($args -> $ex) )
end
getvars(ex::Symbol) = [ex]
function getvars(ex::Expr)
vars = Symbol[]
if ex.head == :call
for arg in ex.args[2:end]
vars = [vars..., getvars(arg)... ]
end
end
return unique(vars)
end
getvars(ex) = [] # fallback
f() = (x = 10; y = 1; println(x * (y - x), " ", fun(x, y) ); g() )
g() = (x = 20; y = 2; println(x * (y - x), " ", fun(x, y) ); h() )
h() = (x = 30; y = 3; println(x * (y - x), " ", fun(x, y) ) )
x = 0; y = 0
ex = :(x * (y - x) )
fun = expr2fun(ex)
julia> methods(fun)
# 1 method for anonymous function "#3":
[1] (::var"#3#4")(x, y) in Main at REPL[1]:4
julia> f()
-90 -90
-360 -360
-810 -810
in essence, use expr2fun(ex)
to turn an expression into a function, and then call locally f(...)
rather then eval(ex)
.
Unfortunately, it has a huge performance penalty:
f1(x, y) = x * (y - x)
f2(x ,y) = fun(x, y)
julia> @btime f1(10, 20);
0.022 ns (0 allocations: 0 bytes)
julia> @btime f2(10, 20);
20.995 ns (0 allocations: 0 bytes)
julia> @btime fun(10, 20);
17.771 ns (0 allocations: 0 bytes)
julia> @code_typed f1(10, 20)
CodeInfo(
1 ─ %1 = Base.sub_int(y, x)::Int64
│ %2 = Base.mul_int(x, %1)::Int64
└── return %2
) => Int64
julia> @code_typed fun(10, 20)
CodeInfo(
1 ─ %1 = Base.sub_int(y, x)::Int64
│ %2 = Base.mul_int(x, %1)::Int64
└── return %2
) => Int64
I do not understand where is the penalty from??? from @code_typed
, f1
and fun
should be the same …