像这样的东西看起来很不错。
unique = fst . foldl' (\(a, b) c -> if (c `elem` b)
then (a, b)
else if (c `elem` a)
then (delete c a, c:b)
else (c:a, b)) ([],[])
折叠的结果元组的第一个元素包含您所期望的,一个包含唯一元素的列表。元组的第二个元素是一个元素是否已经被丢弃时所记住的进程的内存。
关于空间表现。
由于您的问题是设计,因此在显示结果之前,应至少遍历列表的所有元素一次。并且内部算法除了对好的值外,还必须对丢弃的值进行跟踪,但丢弃的值只会出现一次。那么在最坏的情况下,所需的内存量等于输入列表的大小。正如你所说的这种声音商品预期的投入很小。
关于时间表现。
由于预期输入很小且默认未排序,因此尝试将列表排序到算法中是无用的,或者在应用之前是无用的。事实上,我们几乎可以静态地说,将元素放置在其有序位置(放入子列表a
和b
元组中(a,b)
)的额外操作将花费与检查该元素是否出现在列表中相同的时间。
下面是一个更好、更明确的 foldl' 版本。
import Data.List (foldl', delete, elem)
unique :: Eq a => [a] -> [a]
unique = fst . foldl' algorithm ([], [])
where
algorithm (result0, memory0) current =
if (current `elem` memory0)
then (result0, memory0)
else if (current`elem` result0)
then (delete current result0, memory)
else (result, memory0)
where
result = current : result0
memory = current : memory0
在嵌套if ... then ... else ...
指令中,列表result
在最坏的情况下被遍历两次,这可以避免使用以下辅助函数。
unique' :: Eq a => [a] -> [a]
unique' = fst . foldl' algorithm ([], [])
where
algorithm (result, memory) current =
if (current `elem` memory)
then (result, memory)
else helper current result memory []
where
helper current [] [] acc = ([current], [])
helper current [] memory acc = (acc, memory)
helper current (r:rs) memory acc
| current == r = (acc ++ rs, current:memory)
| otherwise = helper current rs memory (r:acc)
但是可以使用 fold 重写助手,如下所示,这绝对更好。
helper current [] _ = ([current],[])
helper current memory result =
foldl' (\(r, m) x -> if x==current
then (r, current:m)
else (current:r, m)) ([], memory) $ result