列表列表中的最短列表(Haskell)

时间:2013-07-26 13:05:39

标签: list haskell short

如何获得列表清单中的最短列表?

我有

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

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

4 个答案:

答案 0 :(得分:6)

首先你已经拥有了:

 shortest [] = []

我实际上并不喜欢这个,因为这意味着

之间没有区别
shortest []

 shortest [[]]

但是,如果你喜欢这种行为,那么Data.List的minimumBy的类型为

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

因此,我们首先需要使用(a -> a -> Ordering) compare以及Data.Function中名为on的有用小函数。 on就像一个“喷嘴”,在将它们输入另一个函数之前将函数应用于2个参数。

 cmp = compare `on` length

这只是给了我们

 shortest = minimumBy cmp

但是当给出一个空列表时,这会中断

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

  shortest [] = Nothing
  shortest ls = Just $ minimumBy cmp ls

答案 1 :(得分: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

答案 2 :(得分: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..]]
  • 您能否从“列表清单中的最短列表”推广到“列表中的最小元素”?

答案 3 :(得分: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的正文。最后一行不断从每个列表中删除一个元素。当看到第一个空列表时,最后一行停止该过程。并且第一行选择首先变为空的原始列表。

与蒂姆解决方案的不同之处:

  • 只有标准函数而不是显式递归的无点样式
  • 在基本情况下不返回任何内容
  • 在“搜索最小列表”阶段没有元组
  • shortest [[1..]]
  • 的不确定