2

非常に高い初期 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
4

2 に答える 2

4

関数が単に別の関数の結果を返す場合、末尾再帰は機能します。この場合、 がありますprimaryColor + traceColorAt(...)。これは、単に関数の値を返すだけでなく、何かを追加していることを意味します。

現在の蓄積された色をパラメーターとして渡すことで、これを修正できます。

于 2011-03-12T07:37:43.773 に答える
4

への再帰呼び出しtraceColorAtは、より大きな式の一部として表示されます。これにより、末尾呼び出しの最適化が妨げられます。これは、traceColorAt戻り後にさらに計算が必要になるためです。

この関数を末尾再帰に変換するには、 のアキュムレータ パラメータを追加しますprimaryColor。への最も外側の呼び出しは (black?)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

于 2011-03-12T07:39:07.543 に答える