【问题标题】:Tail Call Optimization in F#F# 中的尾调用优化
【发布时间】:2015-02-10 20:18:41
【问题描述】:

我真的可以在 F# 中对尾调用优化提供一些帮助。 我正在尝试解析树状结构并在每个叶子上执行计算。

我遇到问题的函数是 calcLength

type Location = float * float
type Radius = float
type Width = float
type Angle = float

type Primitive =      
        | Circle of Location * Radius
        | Ellipse of Location * Radius * Radius
        | Square of Location * Width * Angle
        | MultiPrimitive of Primitive List

type Primitive with
    member x.Length =
        let rec calcLength x =
            match x with
            | Circle (_,r)      -> System.Math.PI * r * 2.
            | Ellipse (_,r1,r2) -> System.Math.PI * 2. * sqrt(  (r1 * r1 ) + (r2 * r2 ) / 2.)
            | Square (_, w,_)   -> w * 4.
            | MultiPrimitive [] -> 0.
            | MultiPrimitive (head::tail) -> calcLength (MultiPrimitive tail) + (calcLength head)

[<Fact>]
let ``test discriminated unions``() =
    let pattern = MultiPrimitive(
                    [ 
                      MultiPrimitive(
                          [ 
                              MultiPrimitive(
                                  [ 
                                    Square( (10.,10.), 10., 45. );
                                    Circle( (3.,7.), 3. );
                                    Circle( (7.,7.), 3. );
                                    Square( (5.,2.), 3., 45. );
                                  ] );

                            Square( (10.,10.), 10., 45. );
                            Circle( (3.,7.), 3. );
                            Circle( (7.,7.), 3. );
                            Square( (5.,2.), 3., 45. );
                          ] );
                      Square( (10.,10.), 10., 45. );
                      Circle( (3.,7.), 3. );
                      Circle( (7.,7.), 3. );
                      Square( (5.,2.), 3., 45. );
                    ] )

    let r = pattern.Length

我尝试通过以下方式使用延续方法:

    let rec calcLength x f =
        match x with
        | Circle (_,r)      -> f() + System.Math.PI * r * 2.
        | Ellipse (_,r1,r2) -> f() + System.Math.PI * 2. * sqrt(  (r1 * r1 ) + (r2 * r2 ) / 2.)
        | Square (_, w,_)   -> f() + w * 4.
        | MultiPrimitive [] -> f()  
        | MultiPrimitive (head::tail) -> calcLength head (fun () -> calcLength(MultiPrimitive tail) f )

    calcLength x (fun () -> 0.)

但是单步执行调试器显示堆栈正在增长,任何帮助都会真正感激不尽。

【问题讨论】:

  • What is tail-recursion?的可能重复
  • 如果您使用的是 Visual Studio,则默认情况下,在调试模式下会禁用尾调用优化,以便为您提供更好的堆栈跟踪。尝试在项目选项中启用此功能。
  • @TomasPetricek 谢谢,好提示!

标签: f# tail-recursion tail-call-optimization


【解决方案1】:

使用 CPS 的常用方法是将结果传递给给定的延续:

        let rec calcLength x k =
            match x with
            | Circle (_,r)      -> k (System.Math.PI * r * 2.)
            | Ellipse (_,r1,r2) -> k (System.Math.PI * 2. * sqrt(  (r1 * r1 ) + (r2 * r2 ) / 2.))
            | Square (_, w,_)   -> k (w * 4.)
            | MultiPrimitive [] -> k 0.
            | MultiPrimitive (head::tail) -> (calcLength head (fun h -> calcLength(MultiPrimitive tail) (fun t -> k (h + t))))

所以在MultiPrimitive 的情况下,您需要传递另一个延续来处理计算头部的结果。

【讨论】:

  • 嗨@Lee,感谢您的回答。为了让您的代码正常工作,我必须将 h 添加到 calcResult。 | MultiPrimitive (head::tail) -&gt; (calcLength head (fun h -&gt; h + calcLength(MultiPrimitive tail) k)) 对吗?
  • @MikeCoxeter - 很抱歉,应该将总和传递给延续 - 请参阅更新。
  • 嗨@Lee,这也可以使用Accumulator Pattern 解决吗?需要调用calcLength 两次,一次用于头部,一次用于尾部,似乎排除了这种模式。
猜你喜欢
  • 2015-01-07
  • 2011-09-13
  • 1970-01-01
  • 2011-04-13
  • 2012-08-19
  • 2011-05-27
  • 2019-04-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多