列表中最短的列表(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` fcomparing 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)的主要内容,如果未能解决你的问题,请参考以下文章

Haskell,树中的列表列表

如何在 haskell 中打印列表?

为啥haskell中的递归列表这么慢?

如何展平haskell中的列表列表

haskell中的循环列表和无限列表有啥区别?

为啥这个 Haskell 代码可以成功地处理无限列表?