Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/fsharp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
F# 堆栈溢出,尽管尾部调用位置不同,但仅在64位_F#_64 Bit_Stack Overflow_32 Bit_Tail Recursion - Fatal编程技术网

F# 堆栈溢出,尽管尾部调用位置不同,但仅在64位

F# 堆栈溢出,尽管尾部调用位置不同,但仅在64位,f#,64-bit,stack-overflow,32-bit,tail-recursion,F#,64 Bit,Stack Overflow,32 Bit,Tail Recursion,起源于,我有一个小F#代码()根据正态分布生成随机值: // val nextSingle : (unit -> float32) let nextSingle = let r = System.Random() r.NextDouble >> float32 // val gauss : (float32 -> float32 -> seq<float32>) let gauss mean stdDev = let rec g

起源于,我有一个小F#代码()根据正态分布生成随机值:

// val nextSingle : (unit -> float32)
let nextSingle =
    let r = System.Random()
    r.NextDouble >> float32

// val gauss : (float32 -> float32 -> seq<float32>)
let gauss mean stdDev =
    let rec gauss ready = seq {
        match ready with
        | Some spare ->
            yield spare * stdDev + mean
            yield! gauss None
        | _ ->
            let rec loop () =
                let u = nextSingle() * 2.f - 1.f
                let v = nextSingle() * 2.f - 1.f
                let s = pown u 2 + pown v 2
                if s >= 1.f || s = 0.f then loop() else
                u, v, s
            let u, v, s = loop()
            let mul = (*)(sqrt(-2.f * log s / s))
            yield mul u * stdDev + mean
            yield! mul v |> Some |> gauss
    }
    gauss None
//val nextingle:(单位->浮动32)
让我们继续=
设r=System.Random()
r、 下一步加倍>>浮动32
//val gauss:(浮动32->浮动32->seq)
设gauss表示stdDev=
让rec gauss ready=seq{
匹配
|一些备用->
备用产量*标准差+平均值
屈服!高斯零
| _ ->
让rec循环()=
设u=nextingle()*2.f-1.f
设v=nextingle()*2.f-1.f
设s=pown u 2+pown v 2
如果s>=1.f | | s=0.f,则循环()else
u、 v,s
设u,v,s=loop()
设mul=(*)(sqrt(-2.f*logs/s))
产量倍数*标准差+平均值
屈服!mul v |>一些|>高斯
}
高斯无
在我看来,这应该只在尾部调用位置调用自身,因此在启用TCO时,不会导致
StackOverflowException
。但在运行64位。运行32位(即“项目设置”中的“首选32位”复选框)时不会显示此选项

我使用的是.NETFramework4.5.2和F#4.4.0.0


有人能解释一下问题的原因吗?

看起来像是编译器序列表达式编译机制中的一个bug。这是一个简化的复制:

let rec loop r = seq {
    if r > 0 then
        let rec unused() = unused()
        yield r
        yield! loop r
}

printfn "%i" (Seq.nth 10000000 (loop 1))

显然,未使用的递归定义的存在不应影响这是否会产生堆栈溢出,但它确实会产生堆栈溢出。

您使用的是哪个.NET版本?我发现这些文章讨论了尾部调用优化vs,也许它们可以帮到忙。还有,您使用的是什么版本的F#?我使用F#4,在调试时,我根本看不到堆栈增长。我反汇编了IL代码,看起来F#4生成的代码不应该堆栈溢出。请包含完整的复制。调用代码是什么样子的?序列表达式中的“尾部递归”可能会令人困惑,因为序列实际上不会自行推进,而其他一些代码会。我已将此作为一个问题提交。是直觉推理还是非直觉推理导致了这个问题?我很想知道,这是否仅仅是使用
IL
inspection解决基本问题,并结合使用VS community edition将分解到我能做到的最小值,还是您使用了更多?我正在努力提高解决TCO问题的能力。如果你想把这作为一个单独的问题,那么我很乐意这样做。还有,你是从哪个版本的代码开始的,GitHub full,GitHub minimal,还是问题中的示例,因为我想独立地找出它。@GuyCoder-我的推理是这样的:在查看计算表达式时谈论“尾部调用”可能会产生误导-真的没有这样的事情(例如,参见一个相关讨论,尽管与序列表达式无关)。因此像
gauss
这样的东西本身不会溢出堆栈,只有调用它的代码才能使堆栈溢出。@GuyCoder-但有一次我看到调用的代码就像
Seq.nth
,这意味着几乎肯定存在编译器错误,因为“tail
模式会产生!
ing”在序列中,表达式应该得到优化(不确定这是否是规范的一部分,但我认为这是众所周知的)。因此,这只是一个看看初始复制的哪些部分是必要的案例。用非递归定义替换原始代码中的
循环
,使复制停止失败,删除模式匹配也是如此。@GuyCoder-我觉得查看IL没有帮助(在序列表达式的编译过程中涉及到很多编译器生成的机器),我只是尝试在源代码级别最小化复制,并对行为进行经验测试。