F#中的分组总数-序列容易,列表也可以吗?
给定一个组id/值元组序列,就可以很容易地计算组总数(与我使用C#和LINQ进行计算的方式大致相同):F#中的分组总数-序列容易,列表也可以吗?,f#,F#,给定一个组id/值元组序列,就可以很容易地计算组总数(与我使用C#和LINQ进行计算的方式大致相同): 但作为F#的新手,我看不出有什么方法可以让列表如此相似。我是否必须使用可变变量,或者是否有一种功能性的方法来处理列表?没有内置的List.groupBy。许多F#内置类型具有分配给所述函数的seq版本的函数。e、 g.来自list.fs let inline sumBy f(list:list)=Seq.sumBy f list 我敢肯定,F#的设计师们就为了一致性而复制什么以及为了干燥而省略
但作为F#的新手,我看不出有什么方法可以让列表如此相似。我是否必须使用可变变量,或者是否有一种功能性的方法来处理列表?没有内置的
List.groupBy
。许多F#内置类型具有分配给所述函数的seq版本的函数。e、 g.来自list.fs
let inline sumBy f(list:list)=Seq.sumBy f list
我敢肯定,F#的设计师们就为了一致性而复制什么以及为了干燥而省略什么进行了很多讨论。我个人希望他们坚持干
如果你想制作自己的“功能”列表,我会使用map和List
let groupBy list =
list
|> List.fold (fun group (g, x) ->
match group |> Map.tryFind g with
| Some(s) -> group |> Map.remove g |> Map.add g (x::s)
| None -> group |> Map.add g [x]
) Map.empty
|> Map.toList
let groupsums = groupBy >> List.map (snd >> List.sum)
如果您只需要总数,则可以跳过保留列表
let groupAndSumBy list =
list
|> List.fold (fun group (g, x) ->
match group |> Map.tryFind g with
| Some(s) -> group |> Map.remove g |> Map.add g (x + s)
| None -> group |> Map.add g x
) Map.empty
|> Map.toList
|> List.map snd
输出
> groupsums items;;
val it : int list = [25; 10]
> groupAndSumBy items;;
val it : int list = [25; 10]
虽然gradbot的解决方案没有问题,但我只想保持简单,并在需要时使用
Seq.toList
将序列转换回列表。因此,您可以将您的定义改写为:
let groupsums =
items
|> Seq.groupBy fst
|> Seq.toList
|> List.map (fun (_,s) -> Seq.sumBy snd s)
虽然我会使用kvb的建议,但如果您打算自己使用,我建议使用
字典
而不是地图
。在我的测试中,它至少快了400%
let groupBy f (list:list<_>) =
let dict = Dictionary()
for v in list do
let k = f v
match dict.TryGetValue(k) with
| true, l -> dict.[k] <- v :: l
| _ -> dict.Add(k, [v])
dict |> Seq.map (|KeyValue|) |> Seq.toList
let groupBy f(列表:列表)=
让dict=Dictionary()
对于列表中的v
设k=fv
将dict.TryGetValue(k)与
|true,l->dict[k]dict.Add(k[v])
dict |>Seq.map(| KeyValue |)|>Seq.toList
或:
let groupSumBy(列表:列表)=
让dict=Dictionary()
对于列表中的k,v
将dict.TryGetValue(k)与
|true,n->dict.[k]dict.Add(k,v)
dict |>Seq.map(| KeyValue |)|>Seq.toList
按参考版本:
let groupSumBy (list:list<_>) =
let dict = Dictionary()
let mutable n = 0
for k, v in list do
match dict.TryGetValue(k, &n) with
| true -> dict.[k] <- v + n
| false -> dict.Add(k, v)
dict |> Seq.map (|KeyValue|) |> Seq.toList
let groupSumBy(列表:列表)=
让dict=Dictionary()
设可变n=0
对于列表中的k,v
将dict.TryGetValue(k和n)与
|true->dict.[k]dict.Add(k,v)
dict |>Seq.map(| KeyValue |)|>Seq.toList
我注意到没有.groupBy;)但也许有一种方法可以实现某种F##的“魔力”,避免分组?另外,在“创建我自己的.groupBy时,我会使用map和list”-列表是什么?列表中有。地图,但没有。list@Sergey我添加了一个可能的解决方案。谢谢。您认为性能或内存占用会有明显的差异吗?我假设groupAndSumBy
是最快的。任何时候,当你使用一堆内置函数来组成一个更大的函数时,你都会付出一定的性能代价。您为此付出了代价,以换取可读性和开发速度。感谢您缩短了符号,但列表在这里有点人为。@Sergey-虽然这是真的,但在进行分组时,没有真正的方法利用列表的结构,因此,使用Seq.groupBy
而不是编写自己的List.groupBy
(在这两种情况下,您都必须使用中间映射结构),您不会损失太多。除非您只需要组折叠的结果(@gradbot answer的第二部分),在这种情况下,您不需要将组保存在内存中。我认为最好的性能/内存解决方案是将一个序列折叠成一个Map.+1,使用TryGetValue的“by ref”版本,您可以在这个基础上再增加15%(我看到@Jon Harrop在某处提到元组模式匹配版本会导致一些额外的堆分配,我在这里看到测试groupBy
)。哇,这个问题对于像我这样的F#noob;)谢谢大家!F#中的映射是不可变的,构建在AVL树上,而字典是可变的,使用哈希映射。通常,可变数据结构比它们的不可变计数器部件更快。我在回答中使用了map,因为问题要求“功能性”解决方案。@daniel我添加了byref版本。不要忘记列表理解。您可以将最后一行替换为[对于dict中的kvp,确实产生kvp.Value]
@gradbot:我想Map
确实提供了一个更具功能性的解决方案,但我很乐意放弃“纯度”以获得4倍的速度提升。
let groupSumBy (list:list<_>) =
let dict = Dictionary()
for k, v in list do
match dict.TryGetValue(k) with
| true, n -> dict.[k] <- v + n
| _ -> dict.Add(k, v)
dict |> Seq.map (|KeyValue|) |> Seq.toList
let groupSumBy (list:list<_>) =
let dict = Dictionary()
let mutable n = 0
for k, v in list do
match dict.TryGetValue(k, &n) with
| true -> dict.[k] <- v + n
| false -> dict.Add(k, v)
dict |> Seq.map (|KeyValue|) |> Seq.toList