Haskell:创建等价列表

时间:2017-07-29 03:48:11

标签: haskell

我想创建一个具有以下定义的函数。

equivalent :: Eq a => (a -> a -> Bool) -> [a] -> [[a]]

等效的定义 三个整数x,y,p
x mod p == y mod p则x和y是等价的 否则x和y不相等。

案例:p = 3,
equivalent eq [1..10]返回列表[[3, 6, 9], [1, 4, 7, 10], [2, 5, 8]]
eq是一个需要两个整数并返回True / False的函数。

您会指导我如何在Haskell中创建函数吗?

2 个答案:

答案 0 :(得分:2)

这是一个递归解决方案。

  1. 弹出列表的第一个元素(x)。
  2. 使用partition将列表的其余部分拆分为 等同于xgroup)的元素和不符合的元素相当于xothers)。
  3. x:group是我们找到的第一个群组,然后我们会对others进行审核。
  4. import Data.List (partition)
    
    equivalent :: (a -> a -> Bool) -> [a] -> [[a]]
    equivalent eq = go
      where
        go [] = []
        go (x:xs) =
          let (group, others) = partition (eq x) xs
          in  (x:group) : go others
    

    证明使用:

    >>> import Data.Function (on)
    >>> equivalent ((==) `on` (`mod` 3)) [1..10]
    [[1,4,7,10],[2,5,8],[3,6,9]]
    

    顺便说一句,这是完成同样事情的另一种方式,我怀疑它更快:

    >>> fmap (fmap snd) . groupBy ((==) `on` fst) . sort . fmap (\i -> (i `mod` 3, i)) $ [1..10]
    [[3,6,9],[1,4,7,10],[2,5,8]]
    

答案 1 :(得分:0)

执行此工作的另一种方法是在列表上使用filter(\\) (difference)函数。所以下面的递归代码就足够了。

groupByRemaindersOf :: Integral a => a -> [a] -> [[a]]
groupByRemaindersOf _ []     = []
groupByRemaindersOf p (x:xs) = let ys = x : filter (\y -> y `rem` p == x `rem` p) xs in ys : groupByRemaindersOf p (xs \\ ys)

*Main> groupByRemaindersOf 3 [1..10]
[[1,4,7,10],[2,5,8],[3,6,9]]

虽然这似乎比涉及sort的任何算法都快,但我相信用Map类型作为累加器折叠可能仍然比这更好。