List Haskell函数用于保留列表中的重复元素
以下是预期的输入/输出: 重复的“密西西比”=“ips” 重复[1,2,3,4,2,5,6,7,1]==[1,2] 重复“==”“ 这是我到目前为止的代码:List Haskell函数用于保留列表中的重复元素,list,sorting,haskell,filter,List,Sorting,Haskell,Filter,以下是预期的输入/输出: 重复的“密西西比”=“ips” 重复[1,2,3,4,2,5,6,7,1]==[1,2] 重复“==”“ 这是我到目前为止的代码: repeated :: String -> String repeated "" = "" repeated x = group $ sort x 我知道代码的最后一部分不起作用。我想对列表进行排序,然后对其进行分组,然后我想对大于1的列表或类似的列表进行筛选。您的代码已经完成了一半的工作
repeated :: String -> String
repeated "" = ""
repeated x = group $ sort x
我知道代码的最后一部分不起作用。我想对列表进行排序,然后对其进行分组,然后我想对大于1的列表或类似的列表进行筛选。您的代码已经完成了一半的工作
> group $ sort "Mississippi"
["M","iiii","pp","ssss"]
你说过要过滤掉不重复的。让我们定义一个谓词,它标识至少包含两个元素的列表:
atLeastTwo :: [a] -> Bool
atLeastTwo (_:_:_) = True
atLeastTwo _ = False
使用此选项:
> filter atLeastTwo . group $ sort "Mississippi"
["iiii","pp","ssss"]
好。现在,我们只需要从这些列表中获取第一个元素。由于列表非空,我们可以安全地使用head
:
> map head . filter atLeastTwo . group $ sort "Mississippi"
"ips"
或者,我们可以将过滤器替换为过滤器(\xs->length xs>=2)
,但这会降低效率
另一种选择是使用列表
> [ x | (x:_y:_) <- group $ sort "Mississippi" ]
"ips"
[x |(x:_y:)好的,好的开始。一个直接的问题是规范要求函数处理数字列表,但您为字符串定义它。列表必须排序,因此其元素必须具有typeclassOrd
。因此,让我们修复类型签名:
repeated :: Ord a => [a] -> [a]
调用sort
和group
后,您将有一个列表,[[a]]
。让我们考虑一下使用filter
的想法。正如您所说,您的谓词应该检查列表中每个列表的长度
,然后将长度
与1进行比较
过滤列表将为您提供一个子集,它是另一个列表列表,类型为[[a]]
。您需要展平此列表。您要做的是将列表中的每个条目映射到它的一个元素。例如,第一个。在序言中有一个函数可以做到这一点
因此,您可以填写以下骨架:
module Repeated (repeated) where
import Data.List (group, sort)
repeated :: Ord a => [a] -> [a]
repeated = map _
. filter (\x -> _)
. group
. sort
我已经用无点风格编写了这篇文章,将过滤谓词作为lambda表达式,但其他许多编写方法也同样不错。找到一种您喜欢的方法!(例如,您也可以用无点风格编写过滤器
谓词,作为两个函数的组合:比较长度
的结果)
当您尝试编译时,编译器将告诉您有两个类型化的孔,即等号右侧的
项。它还将告诉您孔的类型。第一个孔需要一个函数,该函数获取列表并返回单个元素。第二个孔需要使用x
。正确填写这些内容,您的程序就会运行。这里有一些其他方法,可以使用组$sort
评估@chepner对解决方案的评论(这些解决方案看起来更简单,因为一些复杂性隐藏在库例程中。)
虽然排序确实是O(n lg n)
不仅仅是排序,尤其是使用span
的组,它们都建立和销毁临时列表。也就是说,它们这样做:
对未排序列表的线性遍历将需要一些其他数据结构来跟踪所有可能的重复项,并且每个重复项中的查找至少会增加空间复杂性。而精心选择的数据结构可用于维护总体O(n)运行时间,常数可能会使算法在实践中比O(n lg n)解慢
group/span
大大增加了复杂性,因此O(n lg n)不是一个正确的度量
同时大大使实施复杂化
以下所有内容只遍历输入列表一次。是的,它们构建辅助列表。(可能设置将提供更好的性能/更快的查找。)它们可能看起来更复杂,但要将苹果与苹果进行比较,还需要查看组/span
的代码
repeated2, repeated3, repeated4 :: Ord a => [a] -> [a]
repeated2/inserter2
构建一个成对的辅助列表[(a,Bool)]
,其中Bool
为True
如果a
出现多次,则为False
如果到目前为止只出现一次
repeated2 xs = sort $ map fst $ filter snd $ foldr inserter2 [] xs
inserter2 :: Ord a => a -> [(a, Bool)] -> [(a, Bool)]
inserter2 x [] = [(x, False)]
inserter2 x (xb@(x', _): xs)
| x == x' = (x', True): xs
| otherwise = xb: inserter2 x xs
repeated3/inserter3
构建一个成对的辅助列表[(a,Int)]
,其中Int
统计出现了多少个a
。辅助列表仍然被排序,只是为了检查一下
repeated3 xs = map fst $ filter ((> 1).snd) $ foldr inserter3 [] xs
inserter3 :: Ord a => a -> [(a, Int)] -> [(a, Int)]
inserter3 x [] = [(x, 1)]
inserter3 x xss@(xc@(x', c): xs) = case x `compare` x' of
{ LT -> ((x, 1): xss)
; EQ -> ((x', c+1): xs)
; GT -> (xc: inserter3 x xs)
}
repeated4/go4
构建一个已知重复元素的输出列表。它在遍历输入列表时维护一个中间元素列表(到目前为止)。如果遇到重复:它将该元素添加到输出列表中;将其从中间列表中删除;从输入列表的尾部过滤该元素
repeated4 xs = sort $ go4 [] [] xs
go4 :: Ord a => [a] -> [a] -> [a] -> [a]
go4 repeats _ [] = repeats
go4 repeats onces (x: xs) = case findUpd x onces of
{ (True, oncesU) -> go4 (x: repeats) oncesU (filter (/= x) xs)
; (False, oncesU) -> go4 repeats oncesU xs
}
findUpd :: Ord a => a -> [a] -> (Bool, [a])
findUpd x [] = (False, [x])
findUpd x (x': os) | x == x' = (True, os) -- i.e. x' removed
| otherwise =
let (b, os') = findUpd x os in (b, x': os')
(在findUpd
中摆弄列表的最后一位与span
非常相似)
到目前为止听起来不错。你有什么问题吗?我不知道如何筛选列表,我总是会出错。我正在考虑使用以下方法:筛选(\x->length x>1)
或类似的方法。这是步骤1。之后你仍然需要做一些事情(如果你删除了(顺便说一句,不正确)键入repeated
)中的签名。答案不错,但对group
:q只关心一个元素是否重复,而不关心重复多少次。然后对整个列表进行排序似乎也太过了:它可以在获得所需的元素后进行排序(这样会少得多)。类似于nub
的内容是否需要较少的列表遍历?虽然排序确实是O(n lg n),但对未排序列表的线性遍历将需要一些其他数据结构来跟踪所有可能的重复项,并且每个重复项中的查找至少会增加空间复杂度。尽管经过仔细选择