考虑以下这些功能的实现之一,以从列表中删除连续的重复项:
uniq :: Eq a => [a] -> [a]
uniq [] = []
uniq (x:xs) = x:uniq (dropWhile (x ==) xs)
uniq :: Eq a => [a] -> [a]
uniq (x:xs@(y:_))
| x == y = x:tail (uniq xs)
| otherwise = x:uniq xs
uniq l = l
它们在有限列表和无限列表上均按预期工作。更具体地说,对于无限列表,我希望take n $ uniq l
会终止,只要在返回n
值之前就没有无限长的重复值序列。
现在考虑使用foldr
尝试这种功能:
uniq :: (Foldable t, Eq a) => t a -> [a]
uniq = foldr uniqHelper []
where uniqHelper x [] = [x]
uniqHelper x acc@(y:_)
| x == y = acc
| otherwise = x:acc
这在有限列表上正确运行,但永远不会在无限列表上终止,因为uniqHelper
总是需要求值第二个参数。这是可以用更聪明的uniqHelper
来解决的问题,还是天生就不可能使用折叠来完成此任务?
答案 0 :(得分:3)
您可以将元素的删除转移到尾部,因此无论值如何,我们都首先使用“ yield ” x
,然后使用函数(此处{{1 }})评估列表的结尾:
tl
因此,我们首先产生uniq :: (Foldable t, Eq a) => t a -> [a]
uniq = foldr uniqHelper []
where uniqHelper x acc = x : tl acc
where tl acc@(x2:xs) | x == x2 = xs
| otherwise = acc
tl [] = []
,然后再担心下一个元素。如果第二个参数(列表尾部的折叠列表)包含相同的元素,则我们将其“删除”,否则,将保留整个列表。
上面的方法还可以产生x
的第一个元素,例如:
repeat
当然,如果有无限数量的Prelude> head (uniq (repeat 1))
1
Prelude> take 5 $ uniq [1..]
[1,2,3,4,5]
Prelude> uniq [1,1,1,1,2,2,2,1,1,1,1,1,1,3,3,3,3,1,1]
[1,2,1,3,1]
后跟0
,则不会发出1
:
1
答案 1 :(得分:1)
您的第一个代码段产生了第一个x,而第三个代码段产生了最后一个x,这就是差异的原因。
为了如实地将第一个代码片段正确地呈现,我们将其折叠成函数,以便传递状态参数,偶尔将其更新为列表的新唯一元素:
uniq [] = []
uniq (x:xs) = x : foldr g (const []) xs x
where
g y r x | y==x = r x
| otherwise = y : r y
这实际上跳过了重复项,而不是重新进行重复,然后在其他两个答案相同的情况下一遍又一遍地忽略了它们,它们实际上是等效的:dropWhile
只能跳过不超过一个元素,随后的以下reducer调用 its dropWhile (==x)
将跳过该元素。
我始终将r
用作化简器的第二个参数的名称,作为“ r 递归 r esult”的助记符。在这里,r
的定义中g
之后还有另一个参数表示r
是一个函数,我们可以将更新后的值用作状态。
该技术允许使用foldr
对状态计算进行编码,例如take
,drop
,dropWhile
,takeWhile
,etc.。
> head $ uniq $ repeat 1 1 > take 10 $ uniq [n | n <- [1..], n <- replicate n n] [1,2,3,4,5,6,7,8,9,10] > uniq [1..10] [1,2,3,4,5,6,7,8,9,10]
答案 2 :(得分:1)
您可以仅合并您的实现:
sc = SparkContext.getOrCreate()
在无限列表上获得所需的行为:
uniq :: Eq a => [a] -> [a]
uniq = foldr uniqHelper []
where uniqHelper x acc = x : dropWhile (==x) acc