如何使用过滤器查找文本中的相邻单词?

How can I use filter to find neighboring words in a text?

作为借口:在此之前,我编写了一个函数来计算文本中一对单词出现的次数,该函数计算整个文本中的每一对单词。 像这样:

pairCounter =  map (\x -> (head x,length x)). groupTuples . sort

This function returns: [((String, String), Int)] first/second string 在 pair 中 word1/2,Int 是这个可以找到多少次,或者如果你愿意的话,对的“理货”。

我现在想做的是创建一个只 return 任何给定单词的“邻居”的函数。例如:

neighbours [(("red","car"),2),(("house","red"),1)] "red"

应该 return [("car",2),("house",1)] 或对该列表进行一些重新排序。

所以基本上;我们已经建立了任何给定词的所有对,但现在我只想挑出这个词的邻居和它的频率统计。

到目前为止,我想过这样使用过滤器:

filter (\(x, y) -> x /= c || y /= c)
--^(I have no idea if this is syntax correct but it is just to give myself an idea where to start)

但是我发现很难想出一种方法来使用过滤器并包括我的邻居的计数,我的 Int 参数就是。

对于给定的单词 c,您应该保留第一个 String 或第二个 String 等于 c 的项目。我们应该使用 ((s<sub>1</sub>, s<sub>2</sub>), v) 作为模式,因为外部 2 -tuple 的元素是 Strings 的二元组作为第一项,Int 作为第二项。

我们可以使用 concatMap :: Foldable t => (a -> [b]) -> t a -> [b] 并使用一个函数 return [(s<sub>2</sub>, v)] 如果 s<sub>1</sub> 匹配,[(s<sub>1</sub>, v )] if s<sub>2</sub> 匹配,空列表 if none 两个元素匹配:

因此我们过滤:

neighbors :: (Foldable f, Eq a) -> f ((a, a), b) -> a -> [(a, b)]
neighbors items query = concatMap f items
  where f ((s<sub>1</sub>, s<sub>2</sub>), v)
            | query == s<sub>1</sub> = [(s<sub>2</sub>, v)]
            | query == s<sub>2</sub> = [(s<sub>1</sub>, v)]
            | otherwise = []

一种非常惯用的方法是通过列表理解:

neighbours :: Eq a => [((a, a), b)] -> a -> [(a, b)]
neighbours items query =
    [ (neighbor, count)
    | ((s1, s2), count) <- items
    , (self, neighbor) <- [(s1, s2), (s2, s1)]
    , self == query
    ]

实际上,我可能会以其他顺序放置参数以匹配现有库中使用的约定并缩短名称,以便它可以舒适地放在一行中:

neighbours :: Eq a => a -> [((a, a), b)] -> [(a, b)]
neighbours x xs = [(x4, n) | ((x1, x2), n) <- xs, (x3, x4) <- [(x1, x2), (x2, x1)], x3 == x]

我怀疑你不关心顺序的部分会出现在你代码的其他部分,所以另外我会考虑拆分出一个对称的部分。如果稍后您决定对两个订单中出现的对进行归一化并对它们的计数求和或进行类似操作,这也会有所帮助,因为您只需更改一个位置即可将该更新传播给所有消费者。

-- (s)ource, (t)arget, (u)ndirected edge, (w)eight, (w)eighted edge(s)
undirected :: [((a, a), b)] -> [((a, a), b)]
undirected ws = [(u, w) | ((s, t), w) <- ws, u <- [(s, t), (t, s)]]

neighbours :: Eq a => a -> [((a, a), b)] -> [(a, b)]
neighbours x xs = [(t, w) | ((s, t), w) <- undirected xs, s == x]

或者,您可能决定从一开始就使图形无向。

import qualified Data.Map as M

-- export UPair the type but not UPair the constructor
data UPair a = UPair a a
    deriving (Eq, Ord, Read, Show, Functor, Foldable, Traversable)

-- this is strict. can also make a lazy variant
upair :: Ord a => a -> a -> UPair a
upair a a' = if a < a' then UPair a a' else UPair a' a

pairCounter :: [a] -> M.Map (UPair a) Int
pairCounter as = M.fromListWith (+) $ zipWith (\a a' -> (upair a a', 1)) as (tail as)