使用foldl复制删除

时间:2013-01-09 00:45:05

标签: list haskell duplicates duplicate-removal fold

我正在尝试编写remdps函数的实现,它会删除列表中最接近的重复项。例如:"aaabbbsscaa"应该变为"absca"。我必须使用foldl。这是我的尝试:

helper :: Eq a => [a] -> a -> [a]
helper [] ele = [ele]
helper newlist ele = if tail newlist /= ele then newlist:ele
    else newlist

remdps :: Eq a => [a] -> [a]
remdps list = foldl helper [] list

main = putStrLn (show (remdps "aabssscdddeaffff"))

错误:

4.hs:4:41:
    Could not deduce (a ~ [a])
    from the context (Eq a)
      bound by the type signature for helper :: Eq a => [a] -> a -> [a]
      at 4.hs:2:11-33
      `a' is a rigid type variable bound by
          the type signature for helper :: Eq a => [a] -> a -> [a]
          at 4.hs:2:11
    In the second argument of `(/=)', namely `ele'
    In the expression: tail newlist /= ele
    In the expression:
      if tail newlist /= ele then newlist : ele else newlist

4.hs:4:50:
    Could not deduce (a ~ [a])
    from the context (Eq a)
      bound by the type signature for helper :: Eq a => [a] -> a -> [a]
      at 4.hs:2:11-33
      `a' is a rigid type variable bound by
          the type signature for helper :: Eq a => [a] -> a -> [a]
          at 4.hs:2:11
    In the first argument of `(:)', namely `newlist'
    In the expression: newlist : ele
    In the expression:
      if tail newlist /= ele then newlist : ele else newlist

4.hs:4:58:
    Could not deduce (a ~ [a])
    from the context (Eq a)
      bound by the type signature for helper :: Eq a => [a] -> a -> [a]
      at 4.hs:2:11-33
      `a' is a rigid type variable bound by
          the type signature for helper :: Eq a => [a] -> a -> [a]
          at 4.hs:2:11
    In the second argument of `(:)', namely `ele'
    In the expression: newlist : ele
    In the expression:
      if tail newlist /= ele then newlist : ele else newlist
fish: Unknown command './4'
ghc 4.hs; and ./4

问题总是一样的:)。怎么了?

//修改

好的,我有一个正常工作的代码。它使用reverse++,所以它非常难看:)。

helper :: Eq a => [a] -> a -> [a]
helper [] ele = [ele]
helper newlist ele = if head (reverse newlist) /= ele then newlist ++ [ele]
    else newlist

remdps :: Eq a => [a] -> [a]
remdps list = foldl helper [] list

main = putStrLn (show (remdps "aabssscdddeaffff"))

4 个答案:

答案 0 :(得分:2)

您可能尝试做的是:

helper :: Eq a => [a] -> a -> [a]
helper [] ele = [ele]
helper newlist ele = if last newlist /= ele then newlist ++ [ele]
    else newlist

变化:

  • :仅以一种方式工作:左侧是列表的头部(类型a),右侧是尾部(类型[a])。它有时也被称为" cons"。你想要做什么被称为" snoc":在它的右边是列表的最后一个元素(类型a),在左边是初始部分(类型[a]

    " snoc"并不存在于Prelude中,因此您只需以不同的方式编写它:newlist ++ [ele]。 (将此与x : xs == [x] ++ xs进行比较。)

  • tail newlist == ele变为last newlist == eletail获取没有头的列表,但您想要比较newlist的最后一个元素。为此,您有last。 (顺便说一下,要获取列表的初始部分,可以使用init。)

请注意,您还交换了if语句的分支,留下aaa作为答案。 -edit-我看到你了现在更新;)


另请注意,这是一种非常缓慢的方法。每一个" snoc" last的答案会随着remdps的增长而增加,因为Prelude列表在" cons"和head。尝试重写该功能,以便它使用" cons"代替。提示:您在某些时候需要reverse

此外,由于foldl的工作方式,当与无限列表一起使用时,此函数将不起作用。重写此函数以使用foldr代替它可能是一项有趣的练习。

答案 1 :(得分:1)

帮助者的类型注释表明 ele 属于类型
然后你做了以下测试(tail(newlist)== ele),但是如果是类型[a]

如果不同类型,则无法比较两个值。

这不是唯一的错误。

答案 2 :(得分:1)

我建议您查看Data.List的文档。特别是对于tail,您会看到类型为[a] -> [a],因此显然它不会像人们想象的那样返回列表的最后一个元素。

如果您希望从列表中获取单个元素(最后一个),则需要类型为[a] -> a的元素。 haskell的力量源于这一信息几乎足以找到正确的功能。

只需Hoogle it!

P.S。作为旁注 - 这种方法很慢,正如Tinctorius的回答中所提到的那样

答案 3 :(得分:1)

要扩展我的第二条评论,虽然这并没有回答你提出的问题,但我绝不会使用foldl来做这件事。回到我的计划日,我用我的宠物kfoldr功能解决了这个问题,我在这里将其转换为Haskell:

-- |  A special fold that gives you both left and right context at each right
-- fold step.  See the example below.
kfoldr :: (l -> a -> l) -> l -> (l -> a -> r -> r) -> (l -> r) -> [a] -> r
kfoldr advance left combine seedRight [] = seedRight left
kfoldr advance left combine seedRight (x:xs) = combine left x (subfold xs)
where subfold = let newLeft = advance left x 
                in newLeft `seq` kfoldr advance newLeft combine seedRight


removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates = kfoldr advance Nothing step (const [])
    where 
      -- advance is the left context generator, which in this case just 
      -- produces the previous element at each position.
      advance _ x = Just x
      -- step's three arguments in this case are:
      --   (a) the element to the left of current
      --   (b) the current element
      --   (c) the solution for the rest of the list
      step Nothing x xs = x:xs
      step (Just x') x xs
           | x == x' = xs
           | otherwise = x:xs

Haskell的Data.List库有mapAccumLmapAccumR,它们相似但是映射而不是折叠。还有一个密切相关的scanlscanr,可能会用来实现kfoldr(但我没有费心去尝试)。