Reputation: 407
I need to write a code that composes a function, f (x), with itself N times using recursive function.
What I wrote is:
let f x = x + 1 (*it can be any function*)
let rec compose n f x = if n = 0 then
"Can't compose anymore"
else compose (n-1) f (f x);;
which is obviously not right. I know the code is not finished, but I do not know how to continue. Am I on the right path or not? Can you tell me how to solve the problem?
Upvotes: 2
Views: 844
Reputation: 38809
You are on the right path. Based on the requirements, I would try to start from those equations:
compunere 1 f x == f x
The above says that applying f
once to x
is exactly the same as doing (f x)
.
compunere 2 f x == f (f x)
Likewise, applying f
twice should compute f (f x)
. If you replace (f x)
by a call to compunere
, you have:
compunere 2 f x == f (f x) = f (compunere 1 f x)
The general pattern of recursion seems to be:
compunere n f x == f (compunere (n - 1) f x)
Note that the most general type of f
is a -> b
, but when f
is called again with a value of type b
, that means that a
and b
should be the same type, and so f
really is an endomorphism, a function of type a -> a
. That is the case for N >= 1, but in degenerate case of N=0, you could have a different behaviour.
Applying f
zero time to x
could mean "return x", which means that compunere
could theoretically return a value of type a
for zero, for any f
being a a -> b
function, a
and b
possibly distinct; you could distinguish both cases with more code, but here we can simply let the typechecker enforce the constraint that a = b
in all cases and have an uniform behaviour. You can also make 0 invalid (like negative numbers) by throwing an exception (negative applications could theoretically be postitive applications of the inverse function, but you cannot compute that when knowing nothing about f; f could be non-invertible).
Your code is a little bit different:
compunere 3 f x == (compunere 2 f (f x))
== (compunere 1 f (f (f x)))
== (compunere 0 f (f (f (f x))))
...
The advantage of your approach is that the recursive call to compunere
is directly giving the result for the current computation: it is in tail position which allows the compiler to perform tail-call elimination.
When you reach N=0, the value locally bound x
gives the result you want. Here, for N=0 as an input, the only natural interpretation is also to return x
.
Upvotes: 3