为什么递归函数会抛出堆栈溢出异常?

时间:2014-11-11 00:53:07

标签: image-processing f# stack-overflow

我实现了填充孔算法,字节0的值被用作背景,因为我需要先实现一个填充算法算法。
如果我使用check8获取当前像素的TopLeft,Left或BotLeft的位置,我的代码会给我一个Stack Overflow异常。
但如果我只使用Top,TopRight,Right,BotRight和Bot,那就没问题了 我使用仪器分配工具进行了检查,是的,我收到了很多malloc调用。 但我不知道为什么会这样。

let FillHoles (img : Bitmap) =
    let bd = img.LockBits(Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)
    let mutable (p:nativeptr<byte>) = NativePtr.ofNativeInt (bd.Scan0)

    let rec check8 (point:nativeptr<byte>) x y =
        try 
            if (x>=0 && x<=img.Width-1 && y>=0 && y<=img.Height-1)then
                if((NativePtr.get point 0)=(byte 0)) then
                    NativePtr.set point 0 (byte 255)
//                  if (x<>0) then
//                      check8 (NativePtr.add point -4) (x-1) y //Left -4
//                  if (x<>0 && y<>0) then
//                      check8 (NativePtr.add point -(bd.Stride + 4)) (x-1) (y-1) //TopLeft
                    if (y<>0) then
                        check8 (NativePtr.add point -(bd.Stride)) x (y-1) //Top
                    if (x<>img.Width-1 && y<>0) then
                        check8 (NativePtr.add point -(bd.Stride - 4)) (x+1) (y-1)  //TopRight
                    if (x<img.Width-1) then
                        check8 (NativePtr.add point 4) (x+1) y //Right
                    if (x<>img.Width-1 && y<>img.Height-1) then
                        check8 (NativePtr.add point (bd.Stride + 4)) (x+1) (y+1) //BotRight
                    if (y<>img.Height-1) then
                        check8 (NativePtr.add point (bd.Stride)) x (y+1) //Bot
//                    if (x<>0 && y<>img.Height-1) then
//                        check8 (NativePtr.add point (bd.Stride - 4)) (x-1) (y+1) //BotLeft
        with
            | :? System.NullReferenceException as ex -> (printfn "%A" ex.Message)
            | :? System.StackOverflowException as ex -> (printfn "%A" ex.Message)

    for row in 0 .. img.Height-1 do
        for col in 0 .. img.Width-1 do
            if (row=0 || row=img.Height-1 || col=0 || col=img.Width-1) then
                check8 p row col
            p <- NativePtr.add p 4
        done
    done
    img.UnlockBits(bd)
    img

2 个答案:

答案 0 :(得分:2)

在try-catch中不会发生尾调用。请参阅此blog post by the F# Team中的尾部调用是如何编译的?

答案 1 :(得分:0)

我试图做一个尾递归填充但是我没时间了,所以我用堆栈实现了它。
我无法正确调试它的递归,我的IDE表现得很奇怪,所有堆栈帧可能都在搞乱它。
可能有一种更实用的方法。

let FloodFill (img : Bitmap) =
    let bd = img.LockBits(Rectangle(0,0,img.Width,img.Height),ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb)

    let check4 x y =
        let (checkPointer:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*y+x*4)
        if(NativePtr.get checkPointer 0 = byte 0) then
            let st = Stack()
            st.Push(x,y)
            while(st.Count > 0) do
                let current = st.Pop()
                let xx = fst current
                let yy = snd current
                let (pointer:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+xx*4)
                NativePtr.set pointer 0 (byte 255)

                if(xx+1<=img.Width-1)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+(xx+1)*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx+1,yy)
                if(xx-1>=0)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*yy+(xx-1)*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx-1,yy)
                if(yy+1<=img.Height-1)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*(yy+1)+xx*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx,yy+1)
                if(yy-1>=0)then
                    let (point:nativeptr<byte>) = NativePtr.add (NativePtr.ofNativeInt (bd.Scan0)) (bd.Stride*(yy-1)+xx*4)
                    if(NativePtr.get point 0 = byte 0) then
                        st.Push(xx,yy-1)

    for row in 0 .. img.Height-1 do
        for col in 0 .. img.Width-1 do
            if (row=0 || row=img.Height-1 || col=0 || col=img.Width-1) then
                check4 row col
        done
    done
    img.UnlockBits(bd)
    img