2

给定列表列表,如何获得最短的列表?

我有

 shortest :: [[a]] -> [a]
 shortest [] = []

老实说,我真的不知道从那里去哪里感谢任何帮助

4

4 回答 4

6

首先是你已经拥有的:

 shortest [] = []

我实际上不太喜欢这个,因为这意味着两者之间没有区别

shortest []

 shortest [[]]

但是,如果您喜欢这种行为,那么 Data.List 具有minimumBy类型

(a -> a -> Ordering) -> [a] -> a

所以我们首先需要(a -> a -> Ordering)我们可以得到的东西和一个从 Data.Functioncompare调用的有用的小函数。充当某种“喷嘴”,并将一个函数应用于 2 个参数,然后再将它们输入另一个函数。onon

 cmp = compare `on` length

这只是给了我们

 shortest = minimumBy cmp

但是当给定一个空列表时,这会中断,所以

  shortest [] = []
  shortest ls = minimumBy cmp ls

  shortest [] = Nothing
  shortest ls = Just $ minimumBy cmp ls
于 2013-07-26T13:14:25.387 回答
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' :: [(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
于 2013-07-26T23:43:47.733 回答
1

我尝试逐步解释如何“手动”实现此功能。

零列表的最短列表。

该问题已经包含一个空列表的案例。

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..]]
  • 你能从“列表列表中的最短列表”概括为“列表中的最小元素”吗?
于 2013-07-26T17:14:00.363 回答
0

这个解决方案很大程度上基于蒂姆的回答。

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 行停止该过程。第一行选择最先变空的原始列表。

与蒂姆解决方案的区别:

  • 仅具有标准函数而不是显式递归的无点样式
  • 在基本情况下返回 Nothing
  • 在“搜索最小列表”阶段没有元组
  • 不终止shortest [[1..]]
于 2013-07-27T02:05:36.857 回答