2011-08-23 83 views
1

假設我有標記爲A-Z一束組件相互遞歸...我開始通過發送各成分的1.0的值,並且每個組件返回雙(比如說A_0,B_0,..,Z_0)。下一次迭代,我發送總和(1.0 + a_0 + ... + z_0)到每個組件獲得26個新的雙打(a_1,...,z_1)和一個新的值(1.0 + a_0 + ... + z_0 + ... + a_1 + ... + z_1)。計算以這種方式每天都在繼續。與許多參數

的問題是每個組件本身遞歸和依賴於從以前的20天左右的值。所以最明顯的遞歸實現會變得麻煩,因爲組件計算的每個獨立路徑都有一個大量的冗餘遞歸調用。

在我目前的執行情況我分離的成分到誰是負責傳遞來完成計算自己的狀態和使用信息多個代理。我現在處於需要對模型進行更改的位置,並且我發現此實現不靈活。

我的新想法是使用不可變對象來保存每個迭代我克隆我的組件對象,並利用判別工會更新狀態的組件的狀態。即

Component(oldcomponent, [Parameter1(22.0), Parameter14(10.0)]) 

將具有 oldcomponent但更新參數1和14的狀態,使得分量計算的每一個路徑將是易於閱讀,因爲大多數路徑只更新的幾個參數。作爲獎勵,我可以將計算分爲一系列函數,它們將突變列表作爲輸入並輸出新的突變列表。

不過,我覺得這個問題是非常適合於功能性的語言,我有點從功能設計,這是很好的,但偏離我很好奇,別人會怎麼去解決這個問題呢?

編輯:

我想判別聯盟方面是沒有意義的,當我可以使用的「語法」創紀錄的周圍通過。

+0

基於代理的消息傳遞樣式對我來說看起來很好 - 所以當然它的優秀設計是通過20值arround--爲什麼不呢? – Carsten

+0

我會重新思考基於代理的方法,我現在需要對我的模型進行更改,其中涉及使用跨多個代理分佈的參數,我覺得實現此更改會更復雜,然後它應該......只要傳遞大約20個參數,當涉及到玩我的模型時,當我需要添加一個或兩個參數時,它開始覺得多餘,我發現我自己更新了六個遞歸調用來解釋它。 – Jizugu

+0

我認爲這將有助於查看您的代碼。 – Daniel

回答

1

如果我明白你的問題正確,則問題就可以使用下面的代碼示例建模:

//Rec function type to make state full functions 
type RecFn = RecF of (double -> double * RecFn) 

//State type for component A 
type StateA = double 
//Component A 
let compa = 
    let startState : StateA = 1.0 
    let rec run (st:StateA) (i:double) = 
     (i+st) , (RecF (run (st+1.0))) 
    RecF (run startState) 

//State type for component B 
type StateB = double 
//Component B 
let compb = 
    let startState : StateA = 1.0 
    let rec run (st:StateA) (i:double) = 
     (i*st) , (RecF (run (st+1.0))) 
    RecF (run startState) 

//Main rec function 
let rec execute (input : double) (fns : RecFn list) (count:int) = 
    let (vals, newFns) = 
     fns 
     |> List.map (function RecF v -> v input) 
     |> List.unzip 
    match count with 
    | 0 -> vals 
    | _ -> 
     let newSum = (vals |> List.sum) + input 
     execute newSum newFns (count-1) 

//Start with 1.0 as initial value for compa and compb 
execute 1.0 [compa; compb] 5 
|> printfn "%A" 

你的狀態模型將是每個組件的創建記錄類型的最佳方式(在此例如狀態只是一個雙值)。 爲了讓速度更快,您甚至可以使用PSeq模塊,同時將組件應用到double值以獲取double值列表。

我已經使用計數值使其運行5次,萬一您需要運行多天,您可以傳遞一個回調函數代替count並從execute方法調用該回調函數,每次新的double列表被生成,以便回調可以進行進一步處理,如果需要的話。

1

你的基本問題是一個地圖降低問題可以如下解決:

> let next components f x = 
    1.0 + Array.reduce (+) (Array.map (f x) components);; 
val next : 'a [] -> ('b -> 'a -> float) -> 'b -> float 

功能f接受輸入值(例如1.0),並且它必須進行建模的組件。使用輸入值將其映射到每個組件上會得到一組結果。在我們添加1.0之前,使用+函數減少此數組的總和。

您描述的下一個問題是每個組件都需要自己的獨立累加器的變體。這可能寫成:

> let next f (x, accumulators) = 
    let ys = Array.map (f x) accumulators 
    1.0 + Array.sumBy fst ys, Array.map snd ys;; 
val next : 'a [] -> ('b -> 'a -> float) -> 'b -> float 

其中f現在返回包含結果和累加器的對。

請注意,純度在這裏不是有利的。當務之急解決方案只是:

> let next f x (accumulators: _ []) = 
    for i=0 to accumulators.Length-1 do 
     accumulators.[i] <- f(x, snd accumulators.[i]) 
    1.0 + Array.sumBy fst accumulators;; 

其中f現在變異就地累加器。

在我目前的執行情況我分離的成分到誰是負責傳遞完成

我不會用代理這個計算自己的狀態和使用信息多個代理。它們用於併發編程,並沒有關於這個問題的同時發生。