簡體   English   中英

為什么這個F#內部函數不是尾遞歸的?

[英]Why isn't this F# inner function tail-recursive?

如果我用非常高的初始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

如果函數只返回另一個函數的結果,則尾遞歸有效。 在這種情況下,你有primaryColor + traceColorAt(...) ,這意味着它不僅僅是返回函數的值 - 它還向它添加了一些東西。

您可以通過將當前累積的顏色作為參數傳遞來解決此問題。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM