假设我有一堆标记为A-Z的组件...我首先发送每个组件的值为1.0,每个组件返回一个double(比如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.因此,组件计算的每个路径都易于读取,因为大多数路径只更新一些参数。作为奖励,我可以将计算分成一系列函数,这些函数将突变列表作为输入并输出新的突变列表。
但是,我觉得这个问题非常适合功能性语言而且我有点偏离功能设计,这很好,但我很好奇其他人如何解决这个问题?
修改
我认为当我可以使用带有“with syntax”的记录来传递它时,歧视联合方面毫无意义。
答案 0 :(得分: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"
建模状态的最佳方法是为每个组件创建记录类型(在此示例中,状态只是一个double值)。 为了加快速度,您甚至可以在将组件应用于double值时使用PSeq模块来获取double值列表。
我已经使用count值使它运行5次,如果你需要运行很多天,你可以传递一个回调函数代替count,并在每次新的double列表时从execute方法调用该回调函数生成,以便回调可以在需要时进行进一步处理。
答案 1 :(得分: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
现在异地改变累加器。
在我当前的实现中,我将组件分成多个代理,这些代理负责自己的状态并使用消息传递来完成计算
我不会使用代理商。它们用于并发编程,并且没有任何关于这个问题的并发。