Hi,
I am new to Julia coming from Python. In Python we have the “partial” function that allow to derive a function by fixing some arguments. Can we do something similar in Julia?
I would like to do something like:
function f(x, y, z)
x + y + z
end
and then create a function g(x) = f(x, y=computed_y(), z=computed_z())
I can obviously define f as f(x; y…, z=…) and then call f(x) but I have the feeling that computed_y and computed_z are called very time I call f while I would like them to be computed once only.
f(x, y, z) = x + y + z
computed_y() = (println("y_comp"); 2)
computed_z() = (println("z_comp"); 3)
g = let y, z
y = computed_y()
z = computed_z()
g(x) = f(x, y, z)
end
g(1) == 6
The reason is…@tog seems to want one liner, plus he seems to like to know how to interpolate computed_y() stuff into the arguments once and for all, not every time of invocation. Practically, you are right.
When we define g(x) in this way: g(x) = f(x, computed_y(), computed_z())
Everytime we call g(x), computed_y() and computed_z() will have to be calculated.
What if we seek for an expression that looks very much like g(x) = f(x, computed_y(), computed_z()) but that takes the values of computed_x() and computed_y() only at the time of defining g(x)? I thought this was what @tog was really curious about, and @eval g(x) = f(x, $(computed_y()), $(computed_z())) may be close to what he looks for.
I agree eval should be used with care since its evaluation goes into global scope. Definitely the kind of expression I suggested should be discouraged.
@Sijun and @Tamas_Papp
I think you have understood what I want to do i.e not recompute y & z each time the one argument instance is called.
I am not sure I understand what you meant by global scope. I am not sure it is a good idea to make visible to new variables y & z as proposed initially by @Tamas_Papp
I need to understand the let construct does it create the y & z variables and leave only visible inside the let ? are they recomputed each time I call g(something) ?
julia> let x = 1, y = 2
println("inside the let block, we can see x and y: ", x, " ", y)
end
inside the let block, we can see x and y: 1 2
julia> x
ERROR: UndefVarError: x not defined
julia> y
ERROR: UndefVarError: y not defined
No, and we can show it by making the computed_x and y function print something every time they are called, as @Sijun did.
For example:
julia> f(x, y) = x + y
f (generic function with 1 method)
julia> function computed_y()
println("computing y")
2
end
computed_y (generic function with 1 method)
julia> const y = computed_y()
computing y
2
julia> g(x) = f(x, y)
g (generic function with 1 method)
julia> g(1)
3
Notice how computing y is only printed once when we define the variable y. If you want to avoid making the y variable visible, you can hide it in a let block, as suggested above:
julia> const g2 =
let y2 = computed_y()
x -> f(x, y2)
end
computing y
#3 (generic function with 1 method)
julia> g2(1)
3
Again, computing y is only printed once, when you define the function g2.
And in this case, y2 is only visible inside the let block:
Correctly using closure in global scope to get Python semantics of default arguments is rather cumbersome as you need to use the global keyword
let y, z
y = computed_y()
z = computed_z()
global g(x) = f(x, y, z)
end
Note that, without global, you can’t add methods to g.
Comparing this to @eval g(x) = f(x, $(computed_y()), $(computed_z())), I don’t think it’s necessary bad to use @eval, provided that you understand why you need it.