【发布时间】:2013-07-26 13:05:38
【问题描述】:
如何在给定列表的情况下获得最短的列表?
我有
shortest :: [[a]] -> [a]
shortest [] = []
说实话,我真的不知道从那里去哪里 感谢任何帮助
【问题讨论】:
-
一个有趣的方法是函数
snd.head.sort.map((,)=<<length);)
如何在给定列表的情况下获得最短的列表?
我有
shortest :: [[a]] -> [a]
shortest [] = []
说实话,我真的不知道从那里去哪里 感谢任何帮助
【问题讨论】:
snd.head.sort.map((,)=<<length) ;)
首先是你已经拥有的:
shortest [] = []
我其实不是很喜欢这个,因为这意味着两者没有区别
shortest []
和
shortest [[]]
但是,如果您喜欢这种行为,那么 Data.List 的 minimumBy 具有类型
(a -> a -> Ordering) -> [a] -> a
所以我们首先需要(a -> a -> Ordering),我们可以使用compare 和一个有用的小函数on 从Data.Function 获得。 on 就像一个“喷嘴”,将一个函数应用于 2 个参数,然后再将它们输入另一个函数。
cmp = compare `on` length
这只是给了我们
shortest = minimumBy cmp
但是当给定一个空列表时这会中断
shortest [] = []
shortest ls = minimumBy cmp ls
shortest [] = Nothing
shortest ls = Just $ minimumBy cmp ls
【讨论】:
shortest :: [[a]] -> Maybe [a] ; shortest [] = Nothing ; shortest ls = Just $ minimumBy cmp ls
Ord 约束是不必要的
compare `on` f 与comparing f 相同
minimumBy (comparing length) 看起来很不错。它如何在空列表或无限列表元素上中断真是太可惜了。 :(
这是一个可以处理无限列表的版本,并且平均应该花费最少的时间(无论最短列表的位置如何)
此解决方案并行遍历列表,从而避免首先读取非常长(或无限)的列表。如果所有列表都是无限的,那么这个解决方案可能会崩溃。但是,由于它是尾递归的,因此您不应该用完堆栈空间,并且此解决方案应该适用于非常长但有限的列表。
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' 可以简化为:
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' 的实现看起来可以从列表推导中受益。例如,递归调用会变成shortest' [(index, tail list) | (index, list) <- plist, not (null list)]。
我尝试逐步解释如何“手动”实现此功能。
问题已经包含一个空列表的案例。
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..]])的列表会发生什么情况?【讨论】:
这个解决方案很大程度上基于 Tim 的回答。
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..]] 未终止
【讨论】: