具有许多参数的相互递归

时间:2011-08-23 11:31:05

标签: f#

假设我有一堆标记为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”的记录来传递它时,歧视联合方面毫无意义。

2 个答案:

答案 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现在异地改变累加器。

  

在我当前的实现中,我将组件分成多个代理,这些代理负责自己的状态并使用消息传递来完成计算

我不会使用代理商。它们用于并发编程,并且没有任何关于这个问题的并发。