List F#continuations继续执行StackOverflowException

List F#continuations继续执行StackOverflowException,list,f#,continuations,stack-overflow,List,F#,Continuations,Stack Overflow,大家好,我正在实现一个F#函数,它接受两个类型的列表:(int*float)list。这两个列表有不同的长度。 耦合的int元素是一个递增的代码。 我想做的是创建一个新的列表,它将为两个具有相同代码的列表中的每两个元素包含一对(int*float)。需要注意的是,列表中的代码顺序是递增的。 这些列表可能有点长,比如2-3000个元素,所以我尝试使用延续传递样式来实现这个函数,以避免StackOverflowException。但遗憾的是我失败了 这是函数,希望你能给我一些提示 let ident

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

这是函数,希望你能给我一些提示

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

我做错了什么事?

问题在于这一行

produceResult (xs, ys) (fun c -> (code,Math.Abs(rate-rate2))::(k c))
这里您调用continuation,但这个调用不是tail,因为您仍然需要cons(code,Math.Abs(rate-rate2))到(kc)的结果

我想你可以从内到外建立结果列表,然后颠倒最终结果:

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 []

问题就在这方面

produceResult (xs, ys) (fun c -> (code,Math.Abs(rate-rate2))::(k c))
这里您调用continuation,但这个调用不是tail,因为您仍然需要cons(code,Math.Abs(rate-rate2))到(kc)的结果

我想你可以从内到外建立结果列表,然后颠倒最终结果:

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 []
应该是

(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
这也将修复以相反顺序返回的结果

应该是

(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

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

要获得替代答案,请查看以下内容:

该函数获取一对序列并返回根据某个匹配函数匹配的对。我还没有对它进行体积测试


该函数实际上在此处较大的代码段中使用:

要获得替代答案,请查看以下内容:

该函数获取一对序列并返回根据某个匹配函数匹配的对。我还没有对它进行体积测试


该函数实际上在这里的较大代码段中使用:

实际上,通过这些修改,我得到了与以前一样的StackOverflowException。这有点奇怪,因为我以相同的方式实现了它,用于略有不同的任务(一个函数使用两个有序整数列表生成对应的列表),并且它在没有问题的情况下进行尾部递归。我刚刚用两个各为1亿的列表进行了测试,没有得到堆栈溢出。我将列表增加到10亿并耗尽了内存,但仍然没有堆栈溢出。我真的不知道Daniel,我已经完成了复制和粘贴,仍然有堆栈溢出。您可能正在调试模式下运行,这会禁用尾部调用。是的。项目属性>构建>检查生成尾部调用。实际上,通过这些修改,我得到了StackOverflowException。这有点奇怪,因为我以相同的方式实现了它,用于略有不同的任务(一个函数使用两个有序整数列表生成对应的列表),并且它在没有问题的情况下进行尾部递归。我刚刚用两个各为1亿的列表进行了测试,没有得到堆栈溢出。我将列表增加到10亿并耗尽了内存,但仍然没有堆栈溢出。我真的不知道Daniel,我已经完成了复制和粘贴,仍然有堆栈溢出。您可能正在调试模式下运行,这会禁用尾部调用。是的。项目属性>构建>检查生成尾部调用。使用累加器将以相反的顺序输出结果,他当前的实现也会这样做(我假设不是故意的)。使用累加器将以相反的顺序输出结果,他当前的实现也会这样做(我假设不是故意的)。