F# 线程通过展开改变状态
F# Threading Changing State Through Unfold
我正在尝试处理一系列项目,其中流程步骤依赖于先前项目的一些额外累积状态(排序并不重要)。
本质上:
- 我有一个
Seq<'A>
- 我有一个
(Type * int) list
称为 skip list
- 我有一个流程步骤
'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))))
SeqEmpty
和 SeqCons
是活动模式:
let (|SeqEmpty|SeqCons|) (xs: 'a seq) =
if Seq.isEmpty xs then SeqEmpty
else SeqCons(Seq.head xs, Seq.skip 1 xs)
到目前为止,我的过程基本上只是从项目开始,然后向每个项目添加初始跳过,展开并映射剩余的序列以具有相同的项目,但使用新的跳过列表。
我对此有很多问题:
- 简直丑陋又令人困惑
- 我确定它的性能不佳
理想情况下,我希望首先避免将项目映射到包括初始跳过列表的需要,但是我不确定除了将其映射到只是序列中的第一个元素。
可能的替代解决方案
基于 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 最终的跳过列表。
这是对我之前使用的展开方法的巨大改进。
我正在尝试处理一系列项目,其中流程步骤依赖于先前项目的一些额外累积状态(排序并不重要)。
本质上:
- 我有一个
Seq<'A>
- 我有一个
(Type * int) list
称为skip list
- 我有一个流程步骤
'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))))
SeqEmpty
和 SeqCons
是活动模式:
let (|SeqEmpty|SeqCons|) (xs: 'a seq) =
if Seq.isEmpty xs then SeqEmpty
else SeqCons(Seq.head xs, Seq.skip 1 xs)
到目前为止,我的过程基本上只是从项目开始,然后向每个项目添加初始跳过,展开并映射剩余的序列以具有相同的项目,但使用新的跳过列表。
我对此有很多问题:
- 简直丑陋又令人困惑
- 我确定它的性能不佳
理想情况下,我希望首先避免将项目映射到包括初始跳过列表的需要,但是我不确定除了将其映射到只是序列中的第一个元素。
可能的替代解决方案
基于 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 最终的跳过列表。
这是对我之前使用的展开方法的巨大改进。