为什么这个 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
If I call this function with a very high initial currentReflection value I get a stack overflow exception, which indicates that the function is not tail-recursive (correct?). My understanding was that as long as the recursive call was the final computation of the function then it should be compiler-optimized as a tail-recursive function to reuse the current stack frame. Anyone know why this isn't the case here?
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
对
traceColorAt
的递归调用显示为较大表达式的一部分。这会阻止尾部调用优化,因为在traceColorAt
返回后需要进一步计算。要将此函数转换为尾递归,您可以为
primaryColor
添加一个额外的累加器参数。对traceColorAt
的最外层调用将传递primaryColor
的“零”值(黑色?),并且每个递归调用都会对其计算的调整进行求和,例如,代码看起来会是这样例如:如果您希望对调用者隐藏额外的参数,请引入一个执行大部分工作的辅助函数,并从
traceColorAt
调用该函数。The recursive call to
traceColorAt
appears as part of a larger expression. This prevents tail call optimization because further computation is necessary aftertraceColorAt
returns.To convert this function to be tail recursive, you could add an additional accumulator parameter for
primaryColor
. The outermost call totraceColorAt
would pass the "zero" value forprimaryColor
(black?) and each recursive call would sum in the adjustment it computes, e.g. the code would look something like:If you wish to hide the extra parameter from callers, introduce a helper function that performs the bulk of the work and call that from
traceColorAt
.如果函数只是返回另一个函数的结果,则尾递归有效。在本例中,您有
primaryColor + traceColorAt(...)
,这意味着它不只是返回函数的值 - 它还向其中添加一些内容。您可以通过将当前累积的颜色作为参数传递来解决此问题。
Tail recursion works if the function would simply return the result of another function. In this case, you have
primaryColor + traceColorAt(...)
, which means that it is not simply returning the value of the function-- it's also adding something to it.You could fix this by passing the current accumulated color as a parameter.