基于键的功能折叠



我有一个映射reduce代码,我在每个线程中用某个键对其进行分组,然后在reduce部分合并结果。我目前的方法是在累加器中搜索一个特定的密钥索引,然后mapi只检索这个密钥的组合结果,剩下的不修改:

let rec groupFolder sequence acc =
match sequence with
| (by:string, what) :: rest ->
let index = acc |> Seq.tryFindIndex( fun (byInAcc, _) -> byInAcc.Equals(by) )
match index with
| Some (idx) -> 
acc |> Seq.mapi( fun i (byInAcc, whatInAcc) ->  if i = idx then (by, (what |> Array.append whatInAcc) ) else byInAcc, whatInAcc ) 
|> groupFolder rest
| None -> acc |> Seq.append( seq{ yield (by, what) } )
|> groupFolder rest

我的问题是,这是一种更实用的方式来实现这一点吗?

作为该减速器的示例输入

let GroupsCommingFromMap = [| seq { yield! [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |] }, seq { yield! [|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |] }  |];;
GroupsCommingFromMap |> Seq.reduce( fun acc i -> 
acc |> groupFolder (i |> Seq.toList))

预期的结果应该包含所有的key1..key3,每个都有数组1..6

从你发布的代码中,你不太清楚你想做什么。你能包括一些示例输入(以及你想要得到的输出)吗?你的代码真的能处理任何输入吗(它有不完全的模式匹配,所以我怀疑…)

无论如何,您可以使用Seq.groupBy实现基于密钥的映射减少。例如:

let mapReduce mapper reducer input = 
input 
|> Seq.map mapper
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) -> 
k, vs |> Seq.map snd |> Seq.reduce reducer)

此处:

  • mapper从输入序列中获取一个值,并将其转换为键值对。mapReduce函数然后使用键对值进行分组
  • 然后使用reducer来减少与每个键相关联的所有值

这可以让你创建一个像这样的单词计数函数(使用简单的映射器返回单词作为关键字,1作为值,reducer只添加所有数字):

"hello world hello people hello world".Split(' ')
|> mapReduce (fun w -> w, 1) (+)

EDIT:您提到的示例并没有真正的"mapper"部分,而是将数组数组作为输入,因此使用Seq.groupBy直接编写它可能更容易,如下所示:

let GroupsCommingFromMap = 
[| [|("key1", [|1;2;3|] ); ("key2", [|1;2;3|] ); ("key3", [|1;2;3|]) |] 
[|("key1", [|4;5;6|] ); ("key2", [|4;5;6|] ); ("key3", [|4;5;6|]) |]  |]
GroupsCommingFromMap
|> Seq.concat
|> Seq.groupBy fst
|> Seq.map (fun (k, vs) -> k, vs |> Seq.map snd |> Array.concat)

最新更新