Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the standard way to optimise mutual recursion in F#/Scala?

These languages do not support mutually recursive functions optimization 'natively', so I guess it must be trampoline or.. heh.. rewriting as a loop) Do I miss something?

UPDATE: It seems that I did lie about FSharp, but I just didn't see an example of mutual tail-calls while googling

like image 958
Bubba88 Avatar asked May 09 '10 19:05

Bubba88


People also ask

How do you optimize a recursive function?

Bottom-up. Sometimes the best way to improve the efficiency of a recursive algorithm is to not use recursion at all. In the case of generating Fibonacci numbers, an iterative technique called the bottom-up approach can save us both time and space.

What are mutually recursive functions?

Two functions are called mutually recursive if the first function makes a recursive call to the second function and the second function, in turn, calls the first one.

Where are we most likely to find mutual recursion?

Mutual recursion is very common in functional programming, and is often used for programs written in LISP, Scheme, ML, and similar programming languages.

Is tail recursion faster?

As a rule of thumb; tail-recursive functions are faster if they don't need to reverse the result before returning it. That's because that requires another iteration over the whole list. Tail-recursive functions are usually faster at reducing lists, like our first example.


1 Answers

First of all, F# supports mutually recursive functions natively, because it can benefit from the tailcall instruction that's available in the .NET IL (MSDN). However, this is a bit tricky and may not work on some alternative implementations of .NET (e.g. Compact Frameworks), so you may sometimes need to deal with this by hand.

In general, I that there are a couple of ways to deal with it:

  • Trampoline - throw an exception when the recursion depth is too high and implement a top-level loop that handles the exception (the exception would carry information to resume the call). Instead of exception you can also simply return a value specifying that the function should be called again.

  • Unwind using timer - when the recursion depth is too high, you create a timer and give it a callback that will be called by the timer after some very short time (the timer will continue the recursion, but the used stack will be dropped).

    The same thing could be done using a global stack that stores the work that needs to be done. Instead of scheduling a timer, you would add function to the stack. At the top-level, the program would pick functions from the stack and run them.

To give a specific example of the first technique, in F# you could write this:

type Result<´T> =
  | Done of ´T
  | Call of (unit -> ´T)

let rec factorial acc n = 
  if n = 0 then Done acc
  else Call(fun () -> factorial (acc * n) (n + 1))

This can be used for mutually recursive functions as well. The imperative loop would simply call the f function stored in Call(f) until it produces Done with the final result. I think this is probably the cleanest way to implement this.

I'm sure there are other sophisticated techniques for dealing with this problem, but those are the two I know about (and that I used).

like image 118
Tomas Petricek Avatar answered Sep 23 '22 00:09

Tomas Petricek