views:

245

answers:

2

I'm working on problem four of Project Euler and am running into a stackoverflow exception. I'm not asking for help on solving the problem, I'd just like it explained why I am getting a stackoverflow exception. It's usually because of infinite recursion but I don't believe that is the case this time (if I'm just blind and not seeing it right now please let me know).

Here's the code:

let Euler4 =

let reverse sum =
    let rec loop (n,x) =
        if n = 0
        then
            x
        else
            loop (n/10,(x*10) + (n%10))

    loop (sum, 0);

let isPalindrome arg = (arg = (reverse arg));

let findPalindromes (startx,starty) =
    let rec loop (x,y) acc =
        let result = if isPalindrome (x * y) then ((x,y) :: acc) else acc;
        let next = match (x,y) with
            | (x,y) when y = 100 -> (x-1,starty)
            | _ -> (x,y-1)
        if x = 100 then
            result
        else
            loop (next) result

    loop (startx,starty) [];


let value = (999,999);
printfn "%A" (findPalindromes value);

;

A: 

I can't read F# and don't know if your problem is related to this but especially if you're doing something recursive avoid checking for equality to a number, instead try to check for a threshold. For example, instead of testing n = 0, test n <= 0.

JRL
Generally I'd agree with you, but in the case of the reverse function n is always going to end up being 0. The reason for this is that it's an integer and it will drop any remainder, so once n/10 returns a decimal n will end up equaling 0.
justin
+8  A: 

Are you compiling in 'Debug' mode or 'Release' mode? Debug mode in VS has tail calls turned off by default (compiler flag "--tailcalls-"), in order to preserve stacks for debugging, but this has the trade-off that you may StackOverflow. You can either switch to 'Release' mode, or

  • right-click the project properties in Solution Explorer, select 'Properties'
  • go to the 'Build' tab
  • make sure 'Debug' configuration is selected
  • click the box 'Generate tail calls'

to turn on tail calls with 'Debug' settings.

(Your program runs fine for me with tail calls enabled.)

Brian
You my friend are a genius. This fixed my issue. Still, I'm curious how one is supposed to debug legit code if tail call optimization is so important.
justin
If you do the steps I said, you'll still have all the debug settings (symbols, non-optimized, etc.), the only thing that changes is tail calls. With those settings, in the debugger you get the same experience as if you had explicitly written a loop rather than tail recursion (e.g. you won't be able to see the values of all 'previous iterations', since they are overwritten with each call to 'loop', which now appears as a single stack frame). I think this is the best possible experience (a single switch/checkbox lets you choose mode you want; you can't simultaneously have both)...
Brian
... the only issue is what the default should be. We ended up that tail calls are enabled by default in the command-line compiler, and enabled by default with the 'Release' mode project templates, but disabled by default for 'Debug' mode project templates, since these templates are aimed at the best debugging experience, and many programs do not need to rely on TCO for correctness. It's a controversial decision either way. In any case, easy to configure in the UI, and you can even modify your project templates if you find you always prefer 'Debug' to keep tailcalls.
Brian