2012-03-21 91 views
0

(這可能是一個經典的,但我不知道如何以最佳方式表達出來)創建路徑的F#

列表我開始在左邊,在某個日期。 對於一些資產,我可以計算從開始日期到某個未來日期的回報。 從未來的日子,我可以遞歸地進一步在時間上進一步。

我想生成儘可能正確的所有路徑,但在某個目標日期之前停止。

這是我的代碼。 'a是一項資產,並且(DateTime * DateTime)是我爲此基礎報價的兩倍。

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 

由於我使用屈服!我會收集到底每次失敗了一條新路。 所以,我最終必須去除我的序列。 我的問題是:有沒有更好的方法來找到完整的路徑,而沒有重複刪除?

我可以做第二遍或者添加一個List參數,但是有一些「純」的方法可以一次完成這個嗎?

更新

我想我得到了整個做法不妥許多無用的內部循環。 可能矢量化下一個可用的引號會很有用。我將在重構後更新代碼。

更新2

第一重寫是移動產生以下|> 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

我意識到它沒有回答你的問題,但你可能做的一件事是使用類型別名來使你的代碼更容易閱讀。至少在兩個地方使用'a *(DateTime * DateTime)。這可能會更容易閱讀__type datespan <'a> ='a *(DateTime * DateTime)__,然後在您的列表和您的列表中使用datespan。正如我所說,我意識到這不是回答你的問題,但它會讓你的代碼更容易閱讀。 – 2012-03-21 16:47:57

+0

你是完全正確的。感謝您的建議。我將添加一些關於代碼的評論。 – nicolas 2012-03-21 18:49:21

回答

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 } 
+0

對於複製,我們假設我有10個基礎,並且(qt.Value |> fst> dtend)中的所有測試都會導致終止。我將有一個10個空序列的序列。我會研究你的建議,看起來更習慣。謝謝。 – nicolas 2012-03-21 17:52:01

+0

@nicolas啊,我開始明白了 - 所以,基本上,如果'QuoteNextAfterStrict udl dtstart'爲udls中的任何'udl'重新創建與第一個模式(在我的版本中)相匹配的值,那麼您希望產生' pastpath |> List.rev'一次,否則你想讓它產生零次? – 2012-03-21 23:22:14

+0

@nicolas如果是這樣的話,我可能會使用'List.map'在'udls'中的所有值上運行'QuoteNextAfterStrict',然後測試所有的值是否與第一個模式匹配 - 如果是的話,您可以返回'pastpath |> List .rev'。然後你可以迭代那些匹配第二個模式並遞歸的收益。你可以使用'List.partition'將它們分成兩個例子。 – 2012-03-21 23:24:25