识别 Haskell 元组中的重复项
Posted
技术标签:
【中文标题】识别 Haskell 元组中的重复项【英文标题】:Identifying duplicates in Haskell tuples 【发布时间】:2018-05-01 11:34:58 【问题描述】:如果元组中的任何两个值相同,我正在尝试编写一个函数,它将 Nothing
和 Just
Int
元组。对于五个值的元组,这就是我所拥有的。显然,还有改进的余地:
nothingIfMatch :: Maybe (Int, Int, Int, Int, Int) -> Maybe (Int, Int, Int, Int, Int)
nothingIfMatch Nothing = Nothing
nothingIfMatch (Just (a, b, c, d, e))
| a == b = Nothing
| a == c = Nothing
| a == d = Nothing
| a == e = Nothing
| b == c = Nothing
| b == d = Nothing
| b == e = Nothing
| c == d = Nothing
| c == e = Nothing
| d == e = Nothing
| otherwise = Just (a, b, c, d, e)
考虑到一个 n 元组有“n 选择 2”个可能的交集,在这种情况下,只有 10 个选项。但是想象一下这是一个 8 元组,有 28 种可能性,或者是一个 10 元组,有 45 种可能性。
必须有一种更惯用的方式来做到这一点,可能依赖于非确定性特征。
这应该怎么做?
【问题讨论】:
如果您需要在一个结构中存储任意数量的仅一种类型的数据,您可能希望使用某种列表,而不是元组。考虑例如Maybe [Int]
或 Maybe (Vector Int)
【参考方案1】:
我们可以先生成Int
s 的列表,然后执行所有相等性检查:
import Data.List(tails)
twoEqual :: Eq a => [a] -> Bool
twoEqual xs = any (uncurry elem) [(h, t) | (h:t) <- tails xs]
这里我们首先为列表中的每个元素生成一个元组,其中包含该元素和列表的 rest。然后我们执行elem
函数:我们在项目和列表的其余部分调用elem
,如果这些检查中的any
成立,那么我们返回True
,否则返回False
。
所以现在我们可以从这个元组构造一个列表,然后使用一个守卫来执行检查:
nothingIfMatch :: Eq a => Maybe (a, a, a, a, a) -> Maybe (a, a, a, a, a)
nothingIfMatch = (>>= f)
where f r@(a, b, c, d, e) | twoEqual [a, b, c, d, e] = Nothing
| otherwise = Just r
我们可以轻松地向元组添加一个额外的元素,并将其添加到 twoEqual
调用中的列表中。这里我们仍然执行O(n2)。如果我们可以先对元素进行排序,我们可以在 O(n log n) 中完成,或者如果元素是 ,我们甚至可以在 O(n) 中完成em>hashable 并且不会发生哈希冲突。
例如:
-- O(n log n) if the elements can be ordered
import Data.List(sort, tails)
twoEqual :: Ord a => [a] -> Bool
twoEqual xs = or [h1 == h2 | (h1:h2:_) <- tails (sort xs)]
或者如果元素可以被散列:
-- O(n) in case the elements are hashable and no hash collisions
import Data.Hashable(Hashable)
import Data.HashSet(fromList, member)
twoEqual :: (Hashable a, Ord a) => [a] -> Bool
twoEqual xs = any (flip member hs) xs
where hs = fromList xs
【讨论】:
为了将来的读者,介意添加一些有关其工作原理的信息吗? 在“no-Ord
-available”的情况下,可以写twoEqual xs = xs == Data.List.nub xs
。我喜欢它的简洁性和模块化;特别是因为一旦你理解了它,你可以立即用几乎相同的方式写下“Ord
-available”的情况:twoEquals xs_ = xs == Data.List.Ordered.nub xs where xs = sort xs_
。
抱歉,我花了这么长时间才接受这个答案。再读一遍,答案很好!以上是关于识别 Haskell 元组中的重复项的主要内容,如果未能解决你的问题,请参考以下文章