Say I have a function f
, I want to call it repeatedly n
times. I.e. for n = 3
, I want f ∘ f ∘ f
. Is there a nice notation to do this for n
not known at compile time, ideally something like f^n
.
I can’t think of syntaxic sugar that would allow this in Julia. The prettiest way I could think of would be a recursive function :
f_rec(x, n) = (@assert n >= 0; n == 0 ? x : f_rec(f(x), n-1))
Although I heard recursive functions can actually be slow, so I don’t know if this is recommended.
Another way would be metaprogramming, although I don’t find this very clean…
# f^n(x) with n and x defined beforehand
"x" * reduce(*, [" |> f" for _ in 1:n]) |> Meta.parse |> eval
This doesn’t look right. You haven’t defined f
. Something like
apply(f, x, n::Integer) = ((n > 0) ? f(apply(f, x, n-1)) : x)
and, yeah, probably throw an error for negative n
.
Yes, I assumed it was defined before indeed.
You can do a one-liner like
julia> ↑(f, n) = x -> foldl(|>, fill(f, n), init = x)
↑ (generic function with 1 method)
julia> (sqrt ↑ 3)(2)
1.0905077326652577
but the fill
is wasteful so it can be implemented more efficiently.
Recursion can be slow for large n
, and at worst you can get a stack overflow. For small n
it should be fine.
For large n
, an iterative approach should be better, e.g.
function apply(f, n, x::Integer)
if n < 0
error("n must be >= 0.")
elseif n == 0
return x
end
y = f(x)
for _ in 2:n
y = f(y)
end
return y
end
Even so, it would only work for one specific, named function, f
.
Maybe this: ?
This looks like the winner to me. Applied to OP’s case it’s simply:
using IterTools
# Recursively apply function f to value x n times
fn(n,x) = nth(iterated(f,x),n)
We had discussed supporting f^n
, but there wasn’t a consensus in favor:
Another suggested previously is
foldl(|>, Iterators.repeated(f, n))
Edit: Better (as suggested below) is either
F(f,n)=foldl(∘, Iterators.repeated(f, n))
for just the iterated function, or if including the function argument,
F(f,n,x) =foldl(|>, Iterators.repeated(f, n); init=x)
This is how I do it
#=
func_compose(func,n)
Compose a function multiple times
=#
function func_compose(func,n)
newfunc = func
local loop = n
while loop > 1
# help?> ∘
# "∘" can be typed by \circ<tab>
newfunc = func ∘ newfunc
loop -= 1
end
return newfunc
end
f = func_compose(sqrt,5)
println("f(123_456_789.0) = ",f(123_456_789.0))
F(f,n)=foldl(∘, Iterators.repeated(f, n))
F(f,n)=foldl(∘, ntuple(i->f,n))
F(f, n)=n>1 ? ∘(f,F(f,n-1)) : f
I think you forgot the ;init = x
parameter.
More options:
F(f,n) = x->(for _ in 1:n x = f(x) ; end; x) # returns function
F(f,n,x) = (for _ in 1:n x = f(x) ; end; x)
Maybe not very tricky, but do the work without allocating on 1.9.4.