[英]Why does this list comprehension fail?
我試圖從列表中選擇這樣的獨特元素:
x = [1,1,2,3,4]
s = [e | e <- x, not (e `elem` s)]
它不會產生錯誤,但是當我嘗試從s
讀取時,程序似乎掛起了。 為什么?
另外,這樣做的正確方法是什么?
我沒有太大的哈斯克爾兒的,但是這好像你剛剛編寫了東西有點像1 羅素悖論 。 你是不是要求一個列表s
的元素是那些在x
,而不是在s
?
s = [e | e <- [1,1,2,3,4], not (e `elem` s)]
所以,考慮當你試圖詢問s
的第一個元素時會發生什么。 那么, e
的第一個元素是1
,所以如果 not (1 `elem` s)
1
,那么1
將是s
的第一個元素not (1 `elem` s)
。 嗯,是(1 `elem` s)
? 我們可以通過迭代s
的元素並查看是否出現1
來進行檢查。 那么,讓我們從s
的第一個元素開始......
通常假設某些n
是s
的元素。 那一定是真的嗎? n
必須是x
的元素(易於檢查), 也不是s
的元素。 但我們認為它是 s
一個元素。 這是一個矛盾。 因此,沒有n
可以是s
的元素,因此s
必須是空列表。 不幸的是,Haskell編譯器沒有做我們剛剛做的證明,它試圖以編程方式計算s
的元素。
若要從列表中刪除重復項,你希望那尼爾·布朗的評論,推薦功能nub
從Data.List模塊 :
O(n ^ 2) 。 nub函數從列表中刪除重復的元素。 特別是,它只保留每個元素的第一次出現。 (名稱nub的意思是'本質'。)這是nubBy的一個特例,它允許程序員提供自己的相等測試。
請注意,雖然Russel的Paradox有助於暗示這可能是不可計算的,但即使將其更改為s = [e | e <- x, elem es]
s = [e | e <- x, elem es]
。
這是一個有益的手動擴展。 對於任何非空列表, 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)
這是我們無限循環的地方 - 為了評估fx (foldr f [] xs)
我們必須評估fx (foldr f [] xs)
。 你可能會說s
的定義不足以“啟動”它的自我遞歸。 將此與特技fibs
定義進行比較
fibs = 1:1:zipWith (+) fibs (tail fibs)
這是以1:1:...
開始的1:1:...
為了“足夠有效”。 然而,在s
的情況下,沒有(簡單的)方法可以充分發揮作用(請參閱Will Ness在下面的評論,以獲得一個惡魔般的解決方法)。
如果我們沒有那里,它只是切換if
上的分支順序,我們從來沒有達到過。
s = [e | (e:t) <- tails x, not (e `elem` t)]
以上並不是最有效的解決方案,而是展示了如何推理解決方案:為了只包含x的元素一次,我們需要確保它是x中的最后一個元素。 這意味着我們可以搜索列表尾部的元素的出現。 Data.List.tails生成列表的所有子列表,因此我們可以包括子列表的頭部,如果它沒有出現在子列表的其余部分中 - 這是子列表的頭部是最后一個這樣的元素的條件在原始列表中。
如果使用該值的函數是嚴格的(急切),則引用您定義的值可能導致無終止計算。 該函數是嚴格的,如果它總是需要參數的完整值以產生結果。
例如,長度對列表的元素數量是嚴格的 - 但不一定是列表的實際元素。 所以length [[i..] | i <- [1..10]]
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是嚴格的。 為了測試元素的不存在,它需要評估整個列表,這是不可用的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.