簡體   English   中英

為什么這個列表理解失敗了?

[英]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的第一個元素開始......

通常假設某些ns的元素。 那一定是真的嗎? n必須是x的元素(易於檢查), 也不s的元素。 但我們認為它 s一個元素。 這是一個矛盾。 因此,沒有n可以是s的元素,因此s必須是空列表。 不幸的是,Haskell編譯器沒有做我們剛剛做的證明,它試圖以編程方式計算s的元素。

若要從列表中刪除重復項,你希望那尼爾·布朗的評論,推薦功能nubData.List模塊

nub :: Eq a => [a] -> [a] 來源

O(n ^ 2) nub函數從列表中刪除重復的元素。 特別是,它只保留每個元素的第一次出現。 (名稱nub的意思是'本質'。)這是nubBy的一個特例,它允許程序員提供自己的相等測試。


  1. 實際上並不是拉塞爾的悖論; 拉塞爾的悖論是關於一個只包含那些包含自己的集合的集合。 集合不可能存在,因為如果它包含自身,那么它不能包含它自己,如果它不包含它自己,那么它必須包含它自己。

請注意,雖然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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM