Showing posts with label recursion. Show all posts
Showing posts with label recursion. Show all posts

Saturday, June 4, 2011

What's the best way to transform a list with a cumulative function?

I've been doing this kind of thing a lot lately: I have a list of somethings. I want a new list based on that list, but it's not a straight one-to-one map() operation - each value in the resulting list is a function of the corresponding value and all values before it in the input list. (I'm sure there's a one-word name for this type of function, but I don't specialise in maths vocab.)

Example
As an example to discuss, imagine I have a list of numbers, and I want a list of the cumulative totals after each number:
 def main(args: Array[String]) {
val numbers = List(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
val cumulativeTotals = ... ?
println(cumulativeTotals)
}
should output:
List(1, 3, 6, 10, 15, 21, 28, 36, 45, 55)

Funky Folds


So, until the other day, I've usualy been doing this with a foldLeft():
 private def cumulativeTotalFolded(numbers: List[Int]): Seq[Int] = {
numbers.foldLeft((0, List[Int]()))((currentTotalAndCumulativeTotals, n) => {
val (currentTotal, cumulativeTotals) = currentTotalAndCumulativeTotals
(currentTotal + n, (currentTotal + n) :: cumulativeTotals)
})._2.reverse
}
Now, I'm not holding that up as a good example of anything. Folds can be hard to understand at the best of times. A fold that passes around a Tuple2 as the accumulator value is not code that simply communicates to the next guy what's going on.

Stream Simplicity
So, after a particularly hairy instance of one of these the other night, I lay in bed trying to think of a better way. It struck me ('round about 1am) that Streams are a much more natural construct for calculating cumulative functions.

If you haven't come across Streams before, they're basically a way to define a collection recursively be providing the first value in the collection and a function that will calculate the next element in the collection (and, recursively, all the elements after it) as each subsequent element is requested by the client.

Streams are good for this problem because the iteration through the list is simple, while the "next" value usually has easy access to the "previous" value, so to speak. I've used this once or twice now and I like it a lot better.

For the problem above, my solution using a Steram looks like this:
 private def cumulativeTotalStream(numbers: List[Int], total: Int = 0): Stream[Int] = {
numbers match {
case head :: tail =>
Stream.cons(total + head, cumulativeTotalStream(tail, total + head))
case Nil => Stream.Empty
}
}
(Note: to make this work with the println() above, you'll need to toList() the Stream.

Recursion Wrangling
There is, of course, another obvious way, which is to construct the list using a recursive function that passes two accumulators: one for the current total and one for the resulting List of totals that is accumulating:
@tailrec
private def cumulativeTotalRecursive(
numbers: List[Int], currentTotal: Int = 0, cumulativeTotals: List[Int] = Nil): Seq[Int] = {

numbers match {
case head :: tail =>
cumulativeTotalRecursive(tail, currentTotal + head, (currentTotal + head) :: cumulativeTotals)
case Nil => cumulativeTotals.reverse
}
}
There's nothing wrong with this solution, and it probably performs much better than the Stream version, but I feel a bit weird about passing so many parameters around for such a simple operation.

I could reduce the parameter count by getting the currentTotal from the accumulator list instead of passing it around:
 @tailrec
private def cumulativeTotalRecursive2(
numbers: List[Int], cumulativeTotals: List[Int] = Nil): Seq[Int] = {

(numbers, cumulativeTotals) match {
case (number :: tail, Nil) => cumulativeTotalRecursive2(tail, List(number))
case (number :: tail, lastTotal :: otherTotals) =>
cumulativeTotalRecursive2(tail, (number + lastTotal) :: cumulativeTotals)
case (Nil, _) => cumulativeTotals.reverse
}
}
but then the function body ends up more complex than the one with an extra parameter, which isn't a good trade-off.

Mutability the key to Simplicity?
Finally, I thought about the possibility of solving it with a for comprehension. I realised quickly that I'd need a mutable variable, but the result is a very, very simple piece of code:

private def cumulativeTotalComprehension(numbers: List[Int]): Seq[Int] = {
var currentTotal = 0
for (n <- numbers) yield {
currentTotal += n
currentTotal
}
}
I'm pretty sure this wouldn't look as pretty for a lot of the problems I've been tackling with the foldLeft() but, mind you, none of them looked very pretty either. Is this an okay solution? Do Scala afficionados going to vomit in disgust when they see the var keyword?

Your Turn
What I'd really like to know is whether there's an idiomatic way of doing this that I've just never come across.

That's all I can come up with at the moment. I'm sure there's other ways to do it. One that looks simple, takes a sinlge parameter and runs fast would be ideal. If you've got some good ideas of other ways to do this, please leave something in the comments!

Want to learn more?
If all of this has just made you think you might need to do a bit more study on what recursion, Streams or folding are, try one of these great books:

From Amazon...


From Book Depository...


Wednesday, April 7, 2010

Ensuring Tail Call Optimization in Scala

If you're writing functional code you're probably writing recursive code. And if you're writing recursive code in a language that is compiled into imperative instructions - as Scala is - then tail call optimisation becomes very important.

What is a Tail Call?
When you write a recursive function, there are two ways to do it. If your function calls itself recursively and then performs some other operation with the result, it is NOT tail call recursive. On the other hand, if the last thing your function does is call itself recursively, so that the result of the recursive call is the result of the function, that is a tail call recursion, so named because the recursive call is at the "tail" of the function.

Here is an example of both:

object FactorialTest {
def main(args: Array[String]) {
println(factorial_TailCall(5))
println(factorial_NonTailCall(5))
}

def factorial_NonTailCall(n: Int): Int = n match {
case 1 => 1
case _ => factorial_NonTailCall(n - 1) * n
}

def factorial_TailCall(n: Int): Int = {
def f(n: Int, r: Int): Int = n match {
case 1 => r
case _ => f(n - 1, r * n)
}
f(n, 1)
}
}

You can see in factorial_NonTailCall() that, after the function calls itself recursively, it multiplies the result by n and returns the result of that operation. However, in the f() function inside factorial_TailCall(), the recursive call (to f(n - 1, r * n)) is also the result of the function, making this a tail call.

What is Tail Call Optimisation?
Now, if you write your recursive functions using tail calls, that Scala compiler *may* be able to optimise your function. What needs to be optimised? Well, if you've ever written a recursive function in Java - either intentionally or by accident - you may have noticed that, if your function recurses too many times, you get a StackOverflowError. This is because, every time the method calls itself, another frame is added to the stack, and stack memory is relatively limited (compared to heap memory). Scala, running on the JVM, has the same problem - if a method calls itself too many times, it will run out of stack memory.

Scala's tail call optimisation gets around this problem by writing the byte code for tail-call recursive methods in such a way that they don't actually have to call themselves. It compiles the recursive function into something more like a for loop, using a goto operation to jump to the top of the function without actually invoking a method call. If you're interested in reading byte code, you can have a look at the end result at the end of this post.

Ensuring Tail Call Optimisation in Scala
So, tail call recursion is good, because the optimisation is important for your application to not blow up. The theory of tail calls is quite simple, but sometimes in practice it can be quite complex to figure out if you've got it right.

As of Scala 2.8, there is now an easy way to ensure that you function is optimised for tail recursion: the @tailrec annotation. If you add this annotation to any function, the compiler will fail with an error if it cannot perform tail call optimisation on the function.

Pre-Requisites for Tail Call Optimisation
Oddly enough, using tail calls isn't the only requirement for getting your function optimised. The other pre-requisite is that your function has to not be override-able. This is because any function that can be overridden has to be invoked polymorphically by the JVM, which means that any recursion within it has to use a dynamic method invocation and hence can't be optimised to a goto.

If that's all a bit complex, there moral of the story is quite simple: in order for your tail call recursive function to be optimised for tail call optimisation, it also hast to be either private or final (or in a final class or object). So if you've written a tail recursive function and annotated it with @tailrec but can't figure out why the compiler is telling you "error: could not optimize @tailrec annotated method", the first thing to check is that your function can't be overridden.

Remember: private or final.

The Guts
As promised, here's the byte code for the FactorialTest. You can see in the code for the f() method that it never invokes itself, but instead has a "goto 0" command at line 43.


[scala] graham$ javap -private -c -classpath . FactorialTest$
Compiled from "FactorialTest.scala"
public final class FactorialTest$ extends java.lang.Object implements scala.ScalaObject{
public static final FactorialTest$ MODULE$;

public static {};
Code:
0: new #10; //class FactorialTest$
3: invokespecial #13; //Method "":()V
6: return

private FactorialTest$();
Code:
0: aload_0
1: invokespecial #17; //Method java/lang/Object."":()V
4: aload_0
5: putstatic #19; //Field MODULE$:LFactorialTest$;
8: return

private final int f$1(int, int);
Code:
0: iload_1
1: istore 4
3: iload 4
5: iconst_1
6: if_icmpne 31
9: iconst_1
10: ifeq 15
13: iload_2
14: ireturn
15: new #23; //class scala/MatchError
18: dup
19: iload 4
21: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
24: invokevirtual #33; //Method java/lang/Object.toString:()Ljava/lang/String;
27: invokespecial #36; //Method scala/MatchError."":(Ljava/lang/String;)V
30: athrow
31: iconst_1
32: ifeq 46
35: iload_1
36: iconst_1
37: isub
38: iload_2
39: iload_1
40: imul
41: istore_2
42: istore_1
43: goto 0
46: new #23; //class scala/MatchError
49: dup
50: iload 4
52: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
55: invokevirtual #33; //Method java/lang/Object.toString:()Ljava/lang/String;
58: invokespecial #36; //Method scala/MatchError."":(Ljava/lang/String;)V
61: athrow

private int factorial_TailCall(int);
Code:
0: aload_0
1: iload_1
2: iconst_1
3: invokespecial #46; //Method f$1:(II)I
6: ireturn

private int factorial_NonTailCall(int);
Code:
0: iload_1
1: istore_2
2: iload_2
3: iconst_1
4: if_icmpne 30
7: iconst_1
8: ifeq 15
11: iconst_1
12: goto 43
15: new #23; //class scala/MatchError
18: dup
19: iload_2
20: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
23: invokevirtual #33; //Method java/lang/Object.toString:()Ljava/lang/String;
26: invokespecial #36; //Method scala/MatchError."":(Ljava/lang/String;)V
29: athrow
30: iconst_1
31: ifeq 44
34: aload_0
35: iload_1
36: iconst_1
37: isub
38: invokespecial #49; //Method factorial_NonTailCall:(I)I
41: iload_1
42: imul
43: ireturn
44: new #23; //class scala/MatchError
47: dup
48: iload_2
49: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
52: invokevirtual #33; //Method java/lang/Object.toString:()Ljava/lang/String;
55: invokespecial #36; //Method scala/MatchError."":(Ljava/lang/String;)V
58: athrow

public void main(java.lang.String[]);
Code:
0: getstatic #57; //Field scala/Predef$.MODULE$:Lscala/Predef$;
3: aload_0
4: iconst_5
5: invokespecial #59; //Method factorial_TailCall:(I)I
8: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
11: invokevirtual #63; //Method scala/Predef$.println:(Ljava/lang/Object;)V
14: getstatic #57; //Field scala/Predef$.MODULE$:Lscala/Predef$;
17: aload_0
18: iconst_5
19: invokespecial #49; //Method factorial_NonTailCall:(I)I
22: invokestatic #29; //Method scala/runtime/BoxesRunTime.boxToInteger:(I)Ljava/lang/Integer;
25: invokevirtual #63; //Method scala/Predef$.println:(Ljava/lang/Object;)V
28: return

}