F#continuations继续使用StackOverflowException

时间:2011-08-19 16:11:54

标签: list f# continuations stack-overflow

大家好我正在实现一个带有两个类型列表的F#函数:(int * float)list。这两个列表有不同的lentgths。 这对夫妇的int元素是一个增加的代码。 我想要做的是为具有相同代码的两个列表的每两个元素创建一个包含一对(int * float)的新列表。值得注意的是,列表中的代码按顺序递增。 这些列表可能有点长,比如2-3000个元素。所以我尝试使用continuation传递样式来实现这个函数,以避免StackOverflowExceptions。但遗憾的是我失败了。

这是功能,我希望你能给我任何提示!

let identifiedDifference list1 list2 =
    let rec produceResult (l1, l2) k =
        match l1,l2 with
            | [],[] 
            | _,[]
            | [],_ -> k []
            | (code,rate:float)::xs, (code2,rate2)::ys -> 
                if code = code2 
                    then
                        produceResult (xs, ys) (fun c -> (code,Math.Abs(rate-rate2))::(k c))
                    elif code > code2
                        then produceResult (l1, ys) k
                        else produceResult (xs, l2) k
    produceResult (list1, list2) id

我做错了什么?

3 个答案:

答案 0 :(得分:2)

问题出在这一行

produceResult (xs, ys) (fun c -> (code,Math.Abs(rate-rate2))::(k c))

这里你调用continuation但是这个调用不是tail,因为你仍然需要((代码,Math.Abs​​(rate-rate2))到(k c)的结果

我猜你可以从里到外构建结果列表,然后反转最终结果:

let identifiedDifference list1 list2 =
    let rec produceResult (l1, l2) k =
        match l1,l2 with
            | [],[] 
            | _,[]
            | [],_ -> k []
            | (code,rate:float)::xs, (code2,rate2)::ys -> 
                if code = code2 
                    then
                        produceResult (xs, ys) (fun c -> k((code,Math.Abs(rate-rate2))::c))
                    elif code > code2
                        then produceResult (l1, ys) k
                        else produceResult (xs, l2) k
    produceResult (list1, list2) List.rev

修改 在第二次看后我认为这里不需要CPS并且使用累加器应该可以做到这一点:

let identifiedDifference list1 list2 = 
    let rec run l1 l2 acc = 
        match l1, l2 with
        | [], _ | _, [] -> List.rev acc
        | (code1, rate1 : float)::xs, (code2, rate2)::ys ->
            if code1 = code2 then
                run xs ys ((code1, abs (rate1 - rate2))::acc)
            elif code1 > code2 then
                run l1 ys acc
            else
                run xs l2 acc
    run list1 list2 []

答案 1 :(得分:2)

(fun c -> (code,Math.Abs(rate-rate2))::(k c))

应该是

(fun c ->  k ((code,Math.Abs(rate-rate2))::c))

使其尾递归:

let identifiedDifference list1 list2 =
    let rec produceResult (l1, l2) k =
        match l1,l2 with
            | [],[] 
            | _,[]
            | [],_ -> k []
            | (code,rate:float)::xs, (code2,rate2)::ys -> 
                if code = code2 then produceResult (xs, ys) (fun c ->  k ((code,Math.Abs(rate-rate2))::c))
                elif code > code2 then produceResult (l1, ys) k
                else produceResult (xs, l2) k
    produceResult (list1, list2) id

这也将修复以相反顺序返回的结果。

答案 2 :(得分:0)

如需其他答案,请查看以下内容:http://fssnip.net/75

该函数采用几个序列并返回根据某些匹配函数匹配的对。我没有对它进行体积测试。

该功能实际用于此处较大的代码段:http://fssnip.net/76