F# 线程通过展开改变状态

F# Threading Changing State Through Unfold

我正在尝试处理一系列项目,其中流程步骤依赖于先前项目的一些额外累积状态(排序并不重要)。

本质上:

  1. 我有一个Seq<'A>
  2. 我有一个 (Type * int) list 称为 skip list
  3. 我有一个流程步骤'A -> (Type * int) list -> 'B option
    • 这需要当前的跳过列表

有问题的方法本质上是:

Seq<'A'> -> (Type * int) list -> (Type * int) list

所以我们采用一堆输入项和一个初始跳过列表并生成最终跳过列表。

到目前为止我基本上得到了以下内容:

sourceItems
|> Seq.map (fun srcItem -> (srcItem, outerSkip))
|> Seq.unfold (fun elem ->
    match elem with
    | SeqEmpty -> None
    | SeqCons((srcItem, skip), tail) -> 
        match process(srcItem, skip) with
        | Some targetItem -> Some((Some targetItem, skip), tail)
        | None -> Some((None, skip), tail |> Seq.map (fun (i, skp) -> (i, (srcItem.GetType(), liD srcItem) :: skp))))

SeqEmptySeqCons 是活动模式:

let (|SeqEmpty|SeqCons|) (xs: 'a seq) =
    if Seq.isEmpty xs then SeqEmpty
    else SeqCons(Seq.head xs, Seq.skip 1 xs)

到目前为止,我的过程基本上只是从项目开始,然后向每个项目添加初始跳过,展开并映射剩余的序列以具有相同的项目,但使用新的跳过列表。

我对此有很多问题:

  1. 简直丑陋又令人困惑
  2. 我确定它的性能不佳

理想情况下,我希望首先避免将项目映射到包括初始跳过列表的需要,但是我不确定除了将其映射到只是序列中的第一个元素。


可能的替代解决方案

基于 List processing with intermediate state 中采用的不同方法(马克的回答)

我已经能够使用:

items
|> Seq.fold (fun (skip) srcItem ->
    match process(srcItem, skip) with
    | None -> (srcItem.GetType(), liD srcItem) :: skip
    | Some tgtItem ->
        skip
    ) outerSkip

除了有物品时所需的所有东西外,这似乎真的可以解决问题![​​=24=]

这比展开方法显着简单,但我不太清楚它的具体工作原理。

我假设 fun (skip) srcItem -> ... 本质上是在创建一个函数,该函数需要一个额外的参数,通过某些东西的魔力(部分应用程序?)我能够使用 [=] 提供给 fold 22=] - 这样对吗?

我最终采用了问题中提到的弃牌策略。

最终代码为:

let result =
    items
    |> Seq.fold (fun (skip, targetItems), srcItem ->
        match process(srcItem, skip) with
        | None -> ((srcItem.GetType(), getId srcItem) :: skip, targetItems)
        | Some tgtItem -> (skip, tgtItem :: targetItems)) (outerSkip, [])

result 是一个元组 ((Type * int) list, obj list) 这正是我想要的。

然后我可以对目标项目采取行动,并且只是 return 最终的跳过列表。

这是对我之前使用的展开方法的巨大改进。