I have a function, say f(x,y,z)=x+y+z
In many situations, I would like to fix one of the parameters, say letting z=10, and give it another name.
To do this, I can think of several different ways.
g1(x,y)=f(x,y,10)
g2=f
function rename()
g(x,y)=f(x,y,10)
return g
end
g3=rename()
Then, calling g1(x,y), g2(x,y,10) and g3(x,y) should give the same result.
However, the time consuming for these three functions are very different.
A simple test suggest that g1 works far more faster than g2 and g3.
Why is that?
If I have to alias f using g2 or g3 way, how can I make it fast?
Inside a function (i.e. in local scope), they should all be basically the same in performance, and all fast, but the simplest and most idiomatic thing is generally to use an anonymous function (x,y) -> f(x,y,10).
function test1()
for i=1:1000
g1(i,i+1)
end
end
function test2()
for i=1:1000
g2(i,i+1,10)
end
end
function test3()
for i=1:1000
g3(i,i+1)
end
end
@time test1()
@time test2()
@time test3()
The problem is that g2 and g3 are variables in global scope. You either need to define them in the respective functions (test2 and test3), or declare them constant (const g2=f, const g3=rename()).
Also, to time your functions, consider using @btime from BenchmarkTools instead of @time, which includes the compilation time.
That’s unrealistically fast. What if we run the loop a trillion times?
function test1t()
for i=1:10^12
g1(i,i+1)
end
end
julia> @btime test1t()
1.200 ns (0 allocations: 0 bytes)
Still 1.2ns. The compiler realizes that your test function returns nothing, and has no observable effects, and can therefore be replaced with test1() = nothing.
Running a lot of iterations of a loop, for benchmarking purposes, will just trick you. Leave this kind of looping to BenchmarkTools. And do make sure that your benchmarked function returns something that can be observed, otherwise the compiler will go behind your back and replace the function with nothing at all.
Why do you want another function name at all? You can use default arguments for that. And if you find it difficult to remember argument names/order, use optional named arguments in this case f(; x=1, y=2, z=3).
Another option is to define multiple methods for f:
julia> f(x, y, z) = x + y + z
f (generic function with 1 method)
julia> f(x, y) = f(x, y, 3)
f (generic function with 2 methods)
julia> f(x) = f(x, 2, 3)
f (generic function with 3 methods)
julia> f() = f(1, 2, 3)
f (generic function with 4 methods)
julia> f()
6
julia> f(1)
6
julia> f(1,2)
6
julia> f(1,2,3)
6
It’s quite common to want to fix certain arguments at the call site when a function is passed to some higher-order function (e.g. optimization, map, etcetera), rather than at the function-definition site (which you may not even control).
Makes sense. In this case, using an anonymous function (x,y) -> f(x,y,10) as you suggested would be perfect. But the OP didn’t explicitly specify their real use case.