我正在尝试从这样的列表中选择唯一元素:
x = [1,1,2,3,4]
s = [e | e <- x, not (e `elem` s)]
它不会产生错误,但当我尝试从s
读取时,程序似乎挂起了。为什么呢?
另外,这样做的正确方法是什么?
答案 0 :(得分:17)
我不是一个Haskell-er,但这似乎你刚刚编写了类似 1 Russell's paradox的东西。您是否要求列出s
的元素,其中的元素是x
中的元素,而不是s
中的元素?
s = [e | e <- [1,1,2,3,4], not (e `elem` s)]
因此,请考虑当您尝试询问s
的第一个元素时会发生什么。好吧,e
中的第一个元素是1
,因此1
将是s
的第一个元素,如果 not (1 `elem` s)
。嗯,是(1 `elem` s)
?我们可以通过迭代s
的元素并查看1
是否出现来进行检查。好吧,让我们从s
...
通常假设某些n
是s
的元素。那一定是真的吗? n
必须是x
(易于检查)的元素,并且不是 s
的元素。但我们认为 是s
的一个元素。这是一个矛盾。因此,n
可以不是s
的元素,因此s
必须是空列表。不幸的是,Haskell编译器没有做我们刚刚做的证明,它试图以编程方式计算s
的元素。
要从列表中删除重复的项目,您需要Neil Brown在评论中推荐的功能nub
来自Data.List:
nub
::
Eq
a => [a] -> [a]
Source为O(n ^ 2)。 nub函数从列表中删除重复的元素。在 特别是,它只保留每个元素的第一次出现。 (该 名称nub表示'本质'。)这是nubBy的特例,允许 程序员提供自己的平等测试。
答案 1 :(得分:8)
请注意,虽然Russel的Paradox有助于暗示这可能是不可计算的,但即使您将其更改为s = [e | e <- x, elem e s]
,它仍然会失败。
这是一个有益的手动扩展。对于任何非空列表,x
s = [e | e <- x, not (e `elem` s)]
简化为
s = do e <- x
guard (not (e `elem` s))
return e
s = x >>= \e -> if (not (e `elem` s)) then return e else mzero
s = concatMap (\e -> if (not (e `elem` s)) then [e] else []) x
s = foldr ((++) . (\e -> if (not (e `elem` s)) then [e] else [])) [] x
s = foldr (\e xs -> if (not (e `elem` s)) then (e:xs) else xs) [] x
s = foldr (\e ys -> if (e `elem` s) then ys else (e:ys)) [] x
然后我们可以开始评估。由于x
非空,我们可以将其替换为x:xs
并内联foldr
let f = (\e ys -> if (e `elem` s) then ys else (e:ys))
s = f x (foldr f [] xs)
s = (\ys -> if (x `elem` s) then ys else (x:ys)) (foldr f [] xs)
s = (\ys -> if (x `elem` f x (foldr f [] xs)) then ys else (x:ys)) (foldr f [] xs)
这是我们无限循环的地方 - 为了评估f x (foldr f [] xs)
,我们必须评估f x (foldr f [] xs)
。你可能会说s
的定义不够“足够有效”来启动它的自我递归。将此与技巧fibs
定义
fibs = 1:1:zipWith (+) fibs (tail fibs)
以1:1:...
启动,以便“足够高效”。然而,在s
的情况下,没有(简单的)方法可以提高效率(请参阅Will Ness在下面的评论,以获得恶魔般的解决方法)。
如果我们没有那里,它只会切换if
上的分支顺序,这是我们从未达到的分支。
答案 2 :(得分:1)
s = [e | (e:t) <- tails x, not (e `elem` t)]
上述并不是最有效的解决方案,而是展示了如何推理解决方案:为了只包含x的元素一次,我们需要确保它是x中的最后一个元素。这意味着我们可以搜索列表尾部的元素的出现。 Data.List.tails生成列表的所有子列表,因此我们可以包括子列表的头部,如果它没有出现在子列表的其余部分中 - 这是子列表的头部是最后一个这样的元素的条件在原始列表中。
如果使用该值的函数是严格的(急切的),则引用您定义的值可能会导致无终止计算。该函数是严格的,如果它总是需要参数的完整值以产生结果。
例如,length对列表元素的数量是严格的 - 但不一定是列表的实际元素。所以length [[i..] | i <- [1..10]]
终止而不计算列表中元素的值(无限列表。但是,length [[i..] | i <- [1..]]
不会终止,因为为了返回结果,它需要计算所有元素的存在,它永远不会以开放范围结束。
然而,
gtlength :: Int -> [a] -> Ordering
gtlength n [] = n `compare` 0
gtlength 0 xs = GT
gtlength n xs = gtlength (n-1) $ tail xs
甚至可以终止无限列表,因为它不需要评估整个列表。
你的功能因为elem严格而挂起。为了测试元素的不存在,它需要评估整个列表,这是不可用的。