我正在尝试编写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"))
答案 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 == ele
。 tail
获取没有头的列表,但您想要比较newlist
的最后一个元素。为此,您有last
。 (顺便说一下,要获取列表的初始部分,可以使用init
。) 请注意,您还交换了if语句的分支,留下 -edit-我看到你了现在更新;)aaa
作为答案。
另请注意,这是一种非常缓慢的方法。每一个" 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的力量源于这一信息几乎足以找到正确的功能。
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
库有mapAccumL
和mapAccumR
,它们相似但是映射而不是折叠。还有一个密切相关的scanl
和scanr
,可能会用来实现kfoldr
(但我没有费心去尝试)。