列表中最短的列表(Haskell)
Posted
技术标签:
【中文标题】列表中最短的列表(Haskell)【英文标题】:Shortest list in list of list (Haskell) 【发布时间】:2013-07-26 13:05:38 【问题描述】:如何在给定列表的情况下获得最短的列表?
我有
shortest :: [[a]] -> [a]
shortest [] = []
说实话,我真的不知道从那里去哪里 感谢任何帮助
【问题讨论】:
一个有趣的方法是函数snd.head.sort.map((,)=<<length)
;)
【参考方案1】:
首先是你已经拥有的:
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
@SebastianRedl 是的,可能的乐趣,而且 Ord
约束是不必要的
天啊!编辑了我的评论。
compare `on` f
与comparing f
相同
minimumBy (comparing length)
看起来很不错。它如何在空列表或无限列表元素上中断真是太可惜了。 :(【参考方案2】:
这是一个可以处理无限列表的版本,并且平均应该花费最少的时间(无论最短列表的位置如何)
此解决方案并行遍历列表,从而避免首先读取非常长(或无限)的列表。如果所有列表都是无限的,那么这个解决方案可能会崩溃。但是,由于它是尾递归的,因此您不应该用完堆栈空间,并且此解决方案应该适用于非常长但有限的列表。
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)]
。
Toxaris,你不能丢弃任何列表,直到你到达最短的列表的末尾,所以我不知道你怎么知道哪一个不能存活。
好的,我明白了。在我们找到第一个空列表之前,我们什么都不知道。找到之后,我们就知道了完整的答案。【参考方案3】:
我尝试逐步解释如何“手动”实现此功能。
零列表的最短列表。
问题已经包含一个空列表的案例。
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..]]
)的列表会发生什么情况?
您能否将“列表列表中的最短列表”概括为“列表中的最小元素”?
【讨论】:
【参考方案4】:这个解决方案很大程度上基于 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..]]
未终止
【讨论】:
那么您不妨在前面添加一个 fromJust up 以保持原始问题类型签名相同并使用模式匹配处理空列表案例以上是关于列表中最短的列表(Haskell)的主要内容,如果未能解决你的问题,请参考以下文章