希望将一些 Python 代码移植到 F#。我是两种语言的新手。Python 代码使用“Counter”集合,“其中元素存储为字典键,它们的计数存储为字典值”。以 Python 为例:
cnt = Counter()
for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']
cnt[word] += 1
cnt
Counter({'blue': 3, 'red': 2, 'green': 1})
>> # Tally occurrences of words in a list
>> cnt = Counter()
>> for word in ['red', 'blue', 'red', 'green', 'blue', 'blue']:
>> ... cnt[word] += 1
>> cnt
>> Counter({'blue': 3, 'red': 2, 'green': 1})
Run Code Online (Sandbox Code Playgroud)
是否有等效于 Python 的 Counter 集合的 F#?
在 F# 中,诸如此类的状态突变cnt[word] += 1是可能的,但通常会避免。相反,计算是通过使用高阶函数来描述操作的意图来表达的,而不是使用命令式命令来描述机制。在这种情况下,典型的 F# 解决方案可能如下所示:
let counts =
["red"; "blue"; "red"; "green"; "blue"; "blue"]
|> List.groupBy id
|> Map.ofList
|> Map.map (fun _ words -> words |> List.length)
Run Code Online (Sandbox Code Playgroud)
在这里,我们采用相同的单词列表,然后使用List.groupByto 创建列表列表,并将id函数传递给它,说明我们要按值本身(列表中的单词)进行分组。单词列表实际上是 的第二个参数List.groupBy,但我们使用|>("pipe-forward") 运算符将前面的值作为最后一个参数传递给下一个函数。这很有用,因为它允许我们将操作链接在一起,正如我们在下一行中看到的,我们获取 返回的列表列表List.groupBy并将其传递给Map.ofList,这给了我们一个Map<string, string list>.
一Map类允许我们通过一键查找做一样Counter的类型。然后我们通过管道将into的结果转换Map<string, string list>为Map<string, int>带有每个单词计数的a ,这使我们可以传递一个函数,将映射中的值从一种类型转换为另一种类型。在这种情况下,我们希望将值从 a 转换为 an ,因此我们取(我们将其称为“单词”)并将其传递给以获取计数。函数参数 for有两个参数,键和值,但我们只对值感兴趣,所以我通过调用它忽略了键参数。这给了我们一个包含单词作为键和该单词在列表中的计数作为值的 a :Map.ofListMap.mapstring listintstring listList.lengthMap.map_Map<string, int>
val counts : Map<string,int> = map [("blue", 3); ("green", 1); ("red", 2)]
Run Code Online (Sandbox Code Playgroud)
编辑
正如评论中提到的 kvb,F#List模块已经有一个countBy功能,可以将groupBy与结合起来List.length。这使得代码更加简单:
let counts =
["red"; "blue"; "red"; "green"; "blue"; "blue"]
|> List.countBy id
|> Map.ofList
Run Code Online (Sandbox Code Playgroud)
这给出了与上面相同的结果,因为我们获取string * int元组列表并将其通过管道传输到Map.ofList,以便我们获得映射(如 PythonCounter对象)的查找功能。