我有一个映射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)