(这可能是经典,但我想知道如何最好地表达它)
我从左边开始,某个日期。 对于某些资产,我可以计算从开始日期到某个未来日期的回报。 从未来的日期开始,我可以递归地进一步推进。
我想生成尽可能向右走的所有路径,但是在某个目标日期之前停止。
这是我的代码。 '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
答案 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 }