在f#中创建路径列表

时间:2012-03-21 16:06:44

标签: graph f# primitive

(这可能是经典,但我想知道如何最好地表达它)

我从左边开始,某个日期。 对于某些资产,我可以计算从开始日期到某个未来日期的回报。 从未来的日期开始,我可以递归地进一步推进。

我想生成尽可能向右走的所有路径,但是在某个目标日期之前停止。

这是我的代码。 'a是一种资产,而(DateTime * DateTime)是我对所述底层证券报价的2倍。

  member this.getPaths dtstart dtend : Set<('a*(DateTime*DateTime)) list>=
     let rec getPaths dtstart dtend (pastpath:List<'a*(DateTime*DateTime)>) : seq<('a*(DateTime*DateTime)) list>=
        let udls = this.getUnderlyingsQuotingAt dtstart
        let onestep = seq { for udl in udls do
                                let qt = this.QuoteNextAfterSrict udl dtstart 
                                if qt.IsNone || (qt.Value |> fst > dtend) then
                                   yield pastpath |> List.rev  
                                else
                                   let nextdate = qt.Value |> fst 
                                   yield! (getPaths nextdate dtend  ((udl, (dtstart, nextdate))::pastpath) )  } 
        onestep
     getPaths dtstart dtend  List.empty |> Set.ofSeq 

由于我使用yield !,我将为每次失败收集一条新路径。 所以,我必须最终重复我的序列。 我的问题是:有没有更好的方法来找到完整的路径,没有重复数据删除?

我可以进行第二次传递或添加一个List参数,但是有一些“纯粹”的方法可以一次性完成吗?

更新

我认为我对许多无用的内循环都有错误。 可能矢量化下一个可用的引号将是有用的。我将在重构后更新代码。

更新2

第一次重写是以下移动产量|&gt; List.rev上面一层,允许削减不必要的探索。

  member this.getPaths dtstart dtend : Set<('a*(DateTime*DateTime)) list>=
     let count = ref  0

     printfn "computing path from %A to %A " dtstart dtend
     let rec getPaths dtstart dtend (pastpath:List<'a*(DateTime*DateTime)>)  : seq<('a*(DateTime*DateTime)) list>=
        let udls      = this.getUnderlyingsQuotingAt dtstart
        let udlquotes = udls |> Seq.map   (fun udl -> (udl , this.QuoteNextAfterSrict udl dtstart))
                                              |> Seq.filter (fun   (_, q) -> q.IsSome)
                                              |> Seq.map    (fun   (udl, q) -> (udl, q.Value))
                                              |> Seq.filter (fun   (_, q) -> fst q <= dtend  )

        let onestep = seq { if udlquotes.IsEmpty then
                                yield pastpath  |> List.rev  
                            else
                                for (udl, q) in udlquotes  do
                                      let nextdate =  (fst q)
                                      count := !count + 1
                                      if !count%1000 = 0 then printfn "!count  %A , path : %A " !count pastpath
                                      yield! (getPaths nextdate dtend  ((udl, (dtstart,  nextdate))::pastpath)   )
                       }
        onestep
     getPaths dtstart dtend  List.empty |> Set.ofSeq 

1 个答案:

答案 0 :(得分:1)

我不太了解你的算法,但我认为一般结构看起来不错。 “重复数据删除”是什么意思?如果您指的是在递归处理结束时使用的List.rev调用,那么这是非常常见的模式(我认为没有更好的方法可以做到这一点)。

关于F#编码风格,在分析qt值时(因为无法使用内置模式编码条件),您无法轻松使用模式匹配,这有点令人失望。但是,您可以定义一个辅助参数化活动模式,该模式在输入大于参数时匹配:

let inline (|MoreThan|_|) limit input = 
  if input > limit then Some input else None

使用该模式,您可以使seq的正文更具可读性:

let rec getPaths dtstart dtend pastpath = seq {
  let udls = this.getUnderlyingsQuotingAt dtstart 
  for udl in udls do 
    match this.QuoteNextAfterSrict udl dtstart with
    | None 
    | Some (MoreThan dtend _, _) ->
        yield pastpath |> List.rev   
    | Some (nextdate, _) ->
        let newpath = (udl, (dtstart, nextdate))::pastpath
        yield! getPaths nextdate dtend newpath }