尽管有尾调用位置但仅在 64 位中堆栈溢出
Stack overflow despite tail call position but only in 64-bit
源自, I have this little F# code (github) 根据正态分布生成随机值:
// 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
对我来说,这似乎应该只在尾调用位置调用自身,因此在启用 TCO 时永远不会导致 WhosebugException
。但它 在 运行 64 位 时 。它 在 运行 32 位 时 不会 (即项目设置中的“首选 32 位”复选框)。
我正在使用 .NET Framework 4.5.2 和 F# 4.4.0.0。
有人可以解释导致问题的原因吗?
看起来像是编译器的序列表达式编译机制中的错误。这是一个简化的重现:
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))
显然未使用的递归定义的存在不应该影响这是否会产生堆栈溢出,但它会。
源自
// 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
对我来说,这似乎应该只在尾调用位置调用自身,因此在启用 TCO 时永远不会导致 WhosebugException
。但它 在 运行 64 位 时 。它 在 运行 32 位 时 不会 (即项目设置中的“首选 32 位”复选框)。
我正在使用 .NET Framework 4.5.2 和 F# 4.4.0.0。
有人可以解释导致问题的原因吗?
看起来像是编译器的序列表达式编译机制中的错误。这是一个简化的重现:
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))
显然未使用的递归定义的存在不应该影响这是否会产生堆栈溢出,但它会。