为什么不是这个F#内部函数尾部
如果我用非常高的初始currentReflection值调用这个函数,我会得到一个堆栈溢出异常,这表明该函数不是尾递归(正确的?)。 我的理解是,只要递归调用是函数的最终计算,那么它应该被编译器优化为一个尾递归函数来重用当前的栈帧。 任何人都知道为什么这里不是这种情况?
let rec traceColorAt intersection ray currentReflection =
// some useful values to compute at the start
let matrix = intersection.sphere.transformation |> transpose |> invert
let transNormal = matrix.Transform(intersection.normal) |> norm
let hitPoint = intersection.point
let ambient = ambientColorAt intersection
let specular = specularColorAt intersection hitPoint transNormal
let diffuse = diffuseColorAt intersection hitPoint transNormal
let primaryColor = ambient + diffuse + specular
if currentReflection = 0 then
primaryColor
else
let reflectDir = (ray.direction - 2.0 * norm ((Vector3D.DotProduct(ray.direction, intersection.normal)) * intersection.normal))
let newRay = { origin=intersection.point; direction=reflectDir }
let intersections = castRay newRay scene
match intersections with
| [] -> primaryColor
| _ ->
let newIntersection = List.minBy(fun x -> x.t) intersections
let reflectivity = intersection.sphere.material.reflectivity
primaryColor + traceColorAt newIntersection newRay (currentReflection - 1) * reflectivity
对traceColorAt
的递归调用显示为较大表达式的一部分。 这可以防止尾部调用优化,因为traceColorAt
返回后需要进一步的计算。
要将此函数转换为尾递归,您可以为primaryColor
添加一个附加累加器参数。 最外面的呼叫traceColorAt
将通过为“零”值primaryColor
每个递归调用会在它计算的调整,如代码看起来是这样的总结(黑色):
let rec traceColorAt intersection ray currentReflection primaryColor
...
let newPrimaryColor = primaryColor + ambient + diffuse + specular
...
match intersections with
| [] -> newPrimaryColor
| _ ->
...
traceColorAt newIntersection newRay ((currentReflection - 1) * reflectivity) newPrimaryColor
如果您希望隐藏调用者的额外参数,请引入一个辅助函数,该函数执行大量工作并从traceColorAt
调用该traceColorAt
。
如果函数简单地返回另一个函数的结果,则尾递归有效。 在这种情况下,你有primaryColor + traceColorAt(...)
,这意味着它不是简单地返回函数的值 - 它也添加了一些东西。
你可以通过传递当前累积的颜色作为参数来解决这个问题。
链接地址: http://www.djcxy.com/p/80573.html上一篇: Why isn't this F# inner function tail
下一篇: Avoiding stack overflow (with F# infinite sequences of sequences)