[英]unique elements in a haskell list
好的,這可能會出現在序曲中,但是:是否有標准庫 function 用於查找列表中的唯一元素? 為了澄清起見,我的(重新)實施是:
has :: (Eq a) => [a] -> a -> Bool
has [] _ = False
has (x:xs) a
| x == a = True
| otherwise = has xs a
unique :: (Eq a) => [a] -> [a]
unique [] = []
unique (x:xs)
| has xs x = unique xs
| otherwise = x : unique xs
Data.List
的nub
函數(不,實際上它不在Prelude中)絕對可以實現您想要的功能,但是它與您的unique
函數並不完全相同。 它們都保留元素的原始順序,但unique
保留每個元素的最后一次出現,而nub
保留第一次的出現。
如果很重要,您可以執行此操作以使nub
行為完全像unique
一樣(盡管我覺得不是):
unique = reverse . nub . reverse
另外, nub
僅適用於小型列表。 它的復雜度是二次的,因此如果您的列表可以包含數百個元素,則它開始變慢。
如果將類型限制為具有Ord實例的類型,則可以使其擴展性更好。 在nub
上的這種變化仍然保留了列表元素的順序,但是其復雜度為O(n * log n)
:
import qualified Data.Set as Set
nubOrd :: Ord a => [a] -> [a]
nubOrd xs = go Set.empty xs where
go s (x:xs)
| x `Set.member` s = go s xs
| otherwise = x : go (Set.insert x s) xs
go _ _ = []
實際上,已經建議將nubOrd
添加到Data.Set
。
import Data.Set (toList, fromList)
uniquify lst = toList $ fromList lst
我認為unique應該返回僅在原始列表中出現一次的元素列表; 也就是說,原始列表中出現多次的任何元素都不應包含在結果中。
我可以建議一個替代定義unique_alt:
unique_alt :: [Int] -> [Int]
unique_alt [] = []
unique_alt (x:xs)
| elem x ( unique_alt xs ) = [ y | y <- ( unique_alt xs ), y /= x ]
| otherwise = x : ( unique_alt xs )
以下是一些示例,這些示例突出了unique_alt和unqiue之間的區別:
unique [1,2,1] = [2,1]
unique_alt [1,2,1] = [2]
unique [1,2,1,2] = [1,2]
unique_alt [1,2,1,2] = []
unique [4,2,1,3,2,3] = [4,1,2,3]
unique_alt [4,2,1,3,2,3] = [4,1]
我認為這可以做到。
unique [] = []
unique (x:xs) = x:unique (filter ((/=) x) xs)
刪除重復項的另一種方法:
unique :: [Int] -> [Int]
unique xs = [x | (x,y) <- zip xs [0..], x `notElem` (take y xs)]
Haskell中創建唯一列表的算法:
data Foo = Foo { id_ :: Int
, name_ :: String
} deriving (Show)
alldata = [ Foo 1 "Name"
, Foo 2 "Name"
, Foo 3 "Karl"
, Foo 4 "Karl"
, Foo 5 "Karl"
, Foo 7 "Tim"
, Foo 8 "Tim"
, Foo 9 "Gaby"
, Foo 9 "Name"
]
isolate :: [Foo] -> [Foo]
isolate [] = []
isolate (x:xs) = (fst f) : isolate (snd f)
where
f = foldl helper (x,[]) xs
helper (a,b) y = if name_ x == name_ y
then if id_ x >= id_ y
then (x,b)
else (y,b)
else (a,y:b)
main :: IO ()
main = mapM_ (putStrLn . show) (isolate alldata)
輸出:
Foo {id_ = 9, name_ = "Name"}
Foo {id_ = 9, name_ = "Gaby"}
Foo {id_ = 5, name_ = "Karl"}
Foo {id_ = 8, name_ = "Tim"}
我們可以使用 Haskell 編程風格,其中所有循環和遞歸活動都被推出用戶代碼並進入合適的庫函數。 所述庫函數通常以超出 Haskell 初學者技能的方式進行優化。
將問題分解為兩遍的方法如下:
對於第一步,重復元素根本不需要值,所以我們可以使用[Maybe a]
作為第二個列表的類型。 所以我們需要一個 function 類型:
pass1 :: Eq a => [a] -> [Maybe a]
Function pass1
是有狀態列表遍歷的示例,其中state是到目前為止看到的不同元素的列表(或集合)。 對於這類問題,庫提供了mapAccumL:: (s -> a -> (s, b)) -> s -> [a] -> (s, [b])
function。
這里的mapAccumL
function 除了初始的 state 和輸入列表之外,還需要一個類型為s -> a -> (s, Maybe a)
的步驟 function參數。
如果當前元素x
不是重復項,則步驟 function 的 output 是Just x
並且x
被添加到當前 state。如果 x 是重復項,則步驟 function 的 output 是Nothing
,並且 8827141483740 未更改。
在ghci
解釋器下測試:
$ ghci
GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help
λ>
λ> stepFn s x = if (elem x s) then (s, Nothing) else (x:s, Just x)
λ>
λ> import Data.List(mapAccumL)
λ>
λ> pass1 xs = mapAccumL stepFn [] xs
λ>
λ> xs2 = snd $ pass1 "abacrba"
λ> xs2
[Just 'a', Just 'b', Nothing, Just 'c', Just 'r', Nothing, Nothing]
λ>
寫一個pass2
function 就更容易了。 要過濾掉Nothing
非值,我們可以使用:
import Data.Maybe( fromJust, isJust)
pass2 = (map fromJust) . (filter isJust)
但為什么要打擾呢? - 因為這正是catMaybes
庫 function 所做的。
λ>
λ> import Data.Maybe(catMaybes)
λ>
λ> catMaybes xs2
"abcr"
λ>
總的來說,源碼可以寫成:
import Data.Maybe(catMaybes)
import Data.List(mapAccumL)
uniques :: (Eq a) => [a] -> [a]
uniques = let stepFn s x = if (elem x s) then (s, Nothing) else (x:s, Just x)
in catMaybes . snd . mapAccumL stepFn []
這段代碼與無限列表相當兼容,有時被稱為“惰性友好”:
λ>
λ> take 5 $ uniques $ "abacrba" ++ (cycle "abcrf")
"abcrf"
λ>
效率說明:如果我們預計可以在輸入列表中找到許多不同的元素並且我們可以有一個Ord a
實例,則state可以實現為一個Set
object 而不是一個普通列表,這無需改變整體解決方案的結構。
這是一個僅使用 Prelude 函數的解決方案:
uniqueList theList =
if not (null theList)
then head theList : filter (/= head theList) (uniqueList (tail theList))
else []
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.