给定列表列表,如何获得最短的列表?
我有
shortest :: [[a]] -> [a]
shortest [] = []
老实说,我真的不知道从那里去哪里感谢任何帮助
首先是你已经拥有的:
shortest [] = []
我实际上不太喜欢这个,因为这意味着两者之间没有区别
shortest []
和
shortest [[]]
但是,如果您喜欢这种行为,那么 Data.List 具有minimumBy
类型
(a -> a -> Ordering) -> [a] -> a
所以我们首先需要(a -> a -> Ordering)
我们可以得到的东西和一个从 Data.Functioncompare
调用的有用的小函数。充当某种“喷嘴”,并将一个函数应用于 2 个参数,然后再将它们输入另一个函数。on
on
cmp = compare `on` length
这只是给了我们
shortest = minimumBy cmp
但是当给定一个空列表时,这会中断,所以
shortest [] = []
shortest ls = minimumBy cmp ls
shortest [] = Nothing
shortest ls = Just $ minimumBy cmp ls
这是一个可以处理无限列表并且平均应该花费最少时间的版本(无论最短列表的位置如何)
此解决方案并行遍历列表,从而避免首先读取非常长(或无限)的列表。如果所有列表都是无限的,那么这个解决方案可能会崩溃。但是,由于它是尾递归的,因此您不应该用完堆栈空间,并且该解决方案应该适用于非常长但有限的列表。
shortest :: [[a]] -> [a]
shortest [] = []
shortest [soleList] = soleList
shortest lists = lists !! ((fst $ head $ winner) - 1)
where
winner = shortest' $ zipWith (,) [1..] lists
shortest' :: [(Int, [a])] -> [(Int, [a])]
shortest' plist = if (not $ null nullList)
then nullList
else shortest' $ map (\(index, list) -> (index, tail list))
$ filter (not . null . snd) plist
where
nullList = filter (null . snd) plist
请注意当出现多个最短列表时会发生什么:它返回它遇到的第一个。这可能很好,但您实际上可能想要返回多个列表。但是,这超出了当前定义的问题范围,因为您只返回一个列表。
编辑 ------------ 进一步思考,一旦我们得出结论,我们没有最短的列表,那么我们就知道没有一个列表是空的。因此,最短的'可以简化为:
shortest' :: [(Int, [a])] -> [(Int, [a])]
shortest' plist = if (not $ null nullList)
then nullList
else shortest' $ map (\(index, list) -> (index, tail list)) plist
where
nullList = filter (null . snd) plist
我尝试逐步解释如何“手动”实现此功能。
该问题已经包含一个空列表的案例。
shortest :: [[a]] -> [a]
shortest [] = []
不确定这是否完美,但让我们保持原样。
我们必须为只包含一个列表的列表添加一个案例。
shortest [soleList] = ...
显然,如果只有一个列表,那么该列表也是最短的列表。
shortest :: [[a]] -> [a]
shortest [] = []
shortest [soleList] = soleList
我们还需要为更大的列表添加一个案例。
shortest (firstList : remainingLists) = ...
对于最短列表的位置有两种选择:最短列表是firstList
,或者最短列表是 中的列表之一remainingLists
。如果是后者,那么它不仅必须是 中的任何列表remainingLists
,还必须是其中最短的列表。所以我们需要递归调用remainingLists
.
shortest (firstList : remainingLists) =
... firstList ... shortest remainingLists ...
我们应该如何填写...?我们只选择两个列表中较短的一个。
shortest :: [[a]] -> [a]
shortest [] = []
shortest [soleList] = soleList
shortest (firstList : remainingLists) =
shortestOfTwo firstList (shortest remainingLists)
我们如何选择两个列表中较短的一个?
shortestOfTwo :: [[a]] -> [[a]] -> [[a]]
shortestOfTwo firstList secondList = ...
我们只是比较它们的长度!
shortestOfTwo firstList secondList =
if length firstList < length secondList
then firstList
else secondList
这应该可行,但这里有一些关于如何改进它的想法:
length
调用一次?shortest [[1, 2, 3], [1..]]
?这个解决方案很大程度上基于蒂姆的回答。
import Data.List (find)
shortest :: [[a]] -> Maybe [a]
shortest lists
= fmap fst . find (null . snd) . zip lists
. head . filter (any null)
. iterate (map (drop 1)) $ lists
您必须shortest
向后阅读正文。最后一行不断从每个列表中删除一个元素。当看到第一个空列表时,but-last 行停止该过程。第一行选择最先变空的原始列表。
与蒂姆解决方案的区别:
shortest [[1..]]