Haskell 折叠列表并计算任意多个唯一和未知实体的所有出现次数

Haskell 折叠列表并计算任意多个唯一和未知实体的所有出现次数,haskell,fold,Haskell,Fold,我使用Control.Foldl库遍历任意长的列表,并计算任意多个唯一实体的所有出现次数。也就是说,这份清单可能是正式的 [Just "a", Just "b", Just "aab", Nothing, Just "aab"] 我认为我的结果应该是形式的 [(仅“a”,1),(仅“b”,1)(仅“aab”,2),(无,1)] 现在的问题是,我事先没有这些实体的名称,我希望在折叠时动态更新结果 我的问题是,我不知道如何用foldl中的foldab数据类型来描述这种计算。具体来说,在折叠的每一步

我使用
Control.Foldl
库遍历任意长的列表,并计算任意多个唯一实体的所有出现次数。也就是说,这份清单可能是正式的

[Just "a", Just "b", Just "aab", Nothing, Just "aab"]
我认为我的结果应该是形式的

[(仅“a”,1),(仅“b”,1)(仅“aab”,2),(无,1)]

现在的问题是,我事先没有这些实体的名称,我希望在折叠时动态更新结果


我的问题是,我不知道如何用
foldl
中的
foldab
数据类型来描述这种计算。具体来说,在折叠的每一步中,我都需要遍历结果列表并询问我是否看到了当前项目,但我看不到使用
foldl

来描述这一点的方法。折叠允许您遍历列表,同时跟踪某些状态。在本例中,要保持的状态是到目前为止看到的每个字符串的当前计数列表

让我们将此状态建模为
Map String Int
,其中
Map
来自
Data.Map.Strict

如果
m
是我们的当前状态,我们可以执行以下操作:

findWithDefault 0 str m -- returns the count for string str
                           returns 0 if the string isn't found

insert str count m      -- insert the tuple (str,count) into the map
                           (replaces previous value at key str)

empty                   -- the empty map
通过这些操作,我们的折叠步进功能可以如下所示:

step :: Map String Int -> String -> Map String Int
step m str =
  let count = findWithDefault 0 str m
      m' = insert str (count+1) m
  in m'
完整的折叠是:

countStrings :: [String] -> Map String Int
countStrings strs = foldl step empty strs

注意,
Data.Map.Strict
的使用在这里很重要。您希望立即对
count+1
进行评估,而不是存储为thunk。

折叠允许您遍历列表,同时跟踪某些状态。在本例中,要保持的状态是到目前为止看到的每个字符串的当前计数列表

让我们将此状态建模为
Map String Int
,其中
Map
来自
Data.Map.Strict

如果
m
是我们的当前状态,我们可以执行以下操作:

findWithDefault 0 str m -- returns the count for string str
                           returns 0 if the string isn't found

insert str count m      -- insert the tuple (str,count) into the map
                           (replaces previous value at key str)

empty                   -- the empty map
通过这些操作,我们的折叠步进功能可以如下所示:

step :: Map String Int -> String -> Map String Int
step m str =
  let count = findWithDefault 0 str m
      m' = insert str (count+1) m
  in m'
完整的折叠是:

countStrings :: [String] -> Map String Int
countStrings strs = foldl step empty strs

注意,
Data.Map.Strict
的使用在这里很重要。您希望立即计算
count+1
,而不是将其存储为thunk。

考虑按相等值对排序列表进行分组,然后应用lambda函数对出现的次数进行计数

import Data.List

entryCount :: Ord a => [a] -> [(a,Int)]
entryCount = map (\v -> (head v, length v)) . groupBy (==) . sort
因此


考虑按相等值对排序列表进行分组,然后应用lambda函数对出现的次数进行计数

import Data.List

entryCount :: Ord a => [a] -> [(a,Int)]
entryCount = map (\v -> (head v, length v)) . groupBy (==) . sort
因此

那么:

λ> :set -XTupleSections
λ> import qualified Data.Map.Strict as Map
λ> Map.fromListWith (+) $ fmap (,1) [Just "a", Just "b", Just "aab", Nothing, Just "aab"]
fromList [(Nothing,1),(Just "a",1),(Just "aab",2),(Just "b",1)]
我们只需映射列表,形成一对
(x,1)
,然后使用来构建
映射

countOccurences :: (Num a, Ord k) => [k] -> Map.Map k a
countOccurences = Map.fromListWith (+) . fmap (,1)
那么:

λ> :set -XTupleSections
λ> import qualified Data.Map.Strict as Map
λ> Map.fromListWith (+) $ fmap (,1) [Just "a", Just "b", Just "aab", Nothing, Just "aab"]
fromList [(Nothing,1),(Just "a",1),(Just "aab",2),(Just "b",1)]
我们只需映射列表,形成一对
(x,1)
,然后使用来构建
映射

countOccurences :: (Num a, Ord k) => [k] -> Map.Map k a
countOccurences = Map.fromListWith (+) . fmap (,1)

除了其他的答案,我想让你们注意到的概念。它是使用一个操作组合元素序列(包括0长度)的抽象

在这种情况下,幺半群将是元素到数字(它们的计数)的映射,空元素是空映射,组合操作合并两个映射,将两个映射中存在的键的值相加

import Data.Foldable
import qualified Data.Map as M
import Data.Monoid

newtype CountMap k = CountMap { getCountMap :: M.Map k Int }
  deriving (Eq, Ord, Show, Read)

instance (Ord k) => Monoid (CountMap k) where
    mempty = CountMap M.empty
    mappend (CountMap m1) (CountMap m2) = CountMap $ M.unionWith (+) m1 m2

singleton :: k -> CountMap k
singleton x = CountMap $ M.singleton x 1

unique :: (Foldable f, Ord k) => f k -> [(k, Int)]
unique = M.toList . getCountMap . foldMap singleton
虽然使用幺半群描述的解决方案不一定是最短的,但它们通常比折叠更清楚、更高层次地表达主要思想

同样,对于列表以外的结构,例如树,使用幺半群组合元素更自然(在某些情况下更有效):每个叶在幺半群中转换为其相应的值,然后自下而上组合这些值


另请参见。

除了其他答案外,我还想让您注意到。它是使用一个操作组合元素序列(包括0长度)的抽象

在这种情况下,幺半群将是元素到数字(它们的计数)的映射,空元素是空映射,组合操作合并两个映射,将两个映射中存在的键的值相加

import Data.Foldable
import qualified Data.Map as M
import Data.Monoid

newtype CountMap k = CountMap { getCountMap :: M.Map k Int }
  deriving (Eq, Ord, Show, Read)

instance (Ord k) => Monoid (CountMap k) where
    mempty = CountMap M.empty
    mappend (CountMap m1) (CountMap m2) = CountMap $ M.unionWith (+) m1 m2

singleton :: k -> CountMap k
singleton x = CountMap $ M.singleton x 1

unique :: (Foldable f, Ord k) => f k -> [(k, Int)]
unique = M.toList . getCountMap . foldMap singleton
虽然使用幺半群描述的解决方案不一定是最短的,但它们通常比折叠更清楚、更高层次地表达主要思想

同样,对于列表以外的结构,例如树,使用幺半群组合元素更自然(在某些情况下更有效):每个叶在幺半群中转换为其相应的值,然后自下而上组合这些值


另请参见。

此解决方案很好,但每次需要遍历整个列表时,我将计算几个值。这个解决方案很好,但是每次我需要遍历整个列表时,我都要计算几个值。因此使用foldl。这个解决方案也很好,但是每次我需要遍历整个列表时,我都要计算几个值。因此使用foldl。这个解决方案也很好,但是每次我需要遍历整个列表时,我都要计算几个值。因此使用foldl。