【问题标题】:Finding the shortest list in a list of lists in Haskell在 Haskell 的列表中查找最短的列表
【发布时间】:2019-04-03 17:22:36
【问题描述】:

我在使用 Haskell 编写的程序时遇到了一些困难。它背后的想法是递归地在列表列表中找到最短的列表并将其返回。我已经成功地编写了程序,但我似乎无法弄清楚我在其中做错了什么。这些是我尝试编译时遇到的错误:

  • 无法将类型“a”与“[[a]]”匹配,“a”是受类型签名约束的刚性类型变量:shortest :: forall a。 [[a]] -> [a] at shortest.hs:1:13。预期类型:[[[a]]],实际类型:[a]
  • 在“shortest”的第一个参数中,即“y”。在‘(:)’的第一个参数中,即‘shortest y’。在表达式中:最短 y : [list]
  • 相关绑定包括list :: [[a]](绑定在shortest.hs:4:15)、y :: [a](绑定在shortest.hs:4:13)、x :: [a] (在 shortest.hs:4:11 处绑定),shortest :: [[a]] -> [a](在 shortest.hs:2:1 处绑定)。

这是我正在使用的代码:

shortest :: [[a]] -> [a]
shortest [] = []
shortest [y] = y
shortest (x:y:list)
   | length x > length y = shortest y:[list]
   | otherwise = shortest x:[list]

如果有人能给我任何关于我哪里出错的指示,我将不胜感激!

【问题讨论】:

  • 我认为你只需要括号。 shortest (y:[list]) 和 x 情况相同。 : 的优先级使它读起来像 (shortest y) : [list]
  • @jkeuhlen 谢谢!我真的只是发现了自己,看了这么久我没有看到明显的东西!
  • 很高兴有帮助。我将其添加为答案,因为这是此类事物而不是 cmets 更永久的地方。

标签: haskell recursion minimum type-mismatch


【解决方案1】:

我认为您只需要括号:shortest (y:list)x 的情况相同。

: 的优先级使它读起来像 (shortest y) : list

【讨论】:

  • 对,虽然(y:[list]) 也是错误的。你需要纠正那个。 ((1:[2]) == [1,2])。
  • @WillNess 谢谢!现在修好了。我只是将其作为快速评论放在一起,并在 OP 说它对他们有用时将其移至答案。我可能应该在评论->答案转换中做更多的尽职调查。
【解决方案2】:

list 已经是您输入的尾部;您不需要(也不应该)将其包装在另一个列表中。

shortest (x:y:list) = shortest $ (if length x > length y then y else x) : list

在每一步中,问题只是从输入中删除哪个元素xy 到递归调用。

另一种不需要两个基本情况的方法是仅将列表的头部与尾部递归的结果进行比较。

shortest [] = []
shortest (x:xs) = let s = shortest xs
                  in if length s < length x then s else x

最后,元组按字典顺序进行比较,因此您还可以通过用长度标记每个列表,找到最小的标记值,然后提取原始列表来省去显式递归。

shortest = snd . minimum . map (\x -> (length x, x))

使用Control.Arrow,可以将map的参数写成(length &amp;&amp;&amp; id)

最后一种方法的注意事项:由于列表也会按字典顺序进行比较,因此如果您有多个长度最短的列表,最终结果将取决于列表值本身的比较方式。相比之下,前两个例子是稳定的。返回第一个这样的最短列表。


Daniel Wagner 指出了使用 minimum 的更好解决方案,即将每个元素包装在一个 Arg 值中,这样可以比较两个列表的长度,而不考虑列表' 内容。

import Data.Semigroup
shortest xs = x where Arg _ x = minimum [Arg (length x) x | x <- xs]

Arg 基本上是一种 2 元素产品类型,将第一个元素用于其 Ord 实例,而 (,) 则同时使用两者。

【讨论】:

  • 您的最终解决方案的缺点是它需要一个Ord a 约束,并且对于许多相同长度的列表可能会做更多的工作。考虑使用Arg 而不是元组来缓解这两个问题(注意它的instance Ord a =&gt; Ord (Arg a b) 在其上下文中不需要Ord b)。
  • 是的,这比我指出的问题更大。最后有没有更好的方法来解开Arg 值?另外,有没有办法使用ArgMin来简化它?
  • 要获得完整的工作解决方案,请考虑shortest xs = x where Arg _ x = minimum [Arg (length x) x | x &lt;- xs]。你可以使用ArgMin,但它在语法上有点吵。
【解决方案3】:
  shortest []=[]
  shortest [y] = y
  shortest (x:y:list)
   |length x > length y = shortest (y:list)            
   |otherwise = shortest (x:list)

这很有效 :),还值得一提的是,如果列表中有 2 个或多个“最短”元素,则第一个元素总是会弹出。

   Prelude>shortest[[1],[2],[3]]
   [1]

【讨论】:

    【解决方案4】:
    import Data.List
    import Data.Ord
    shortest list = minimumBy (comparing length) list
    

    无积分:

    shortest = minimumBy (comparing length)
    

    这些库包含在 GHC 中。他们的名字说明了他们做得很好。也许为空列表添加一个单独的案例。

    【讨论】:

    • 甚至minimumBy $ comparing length
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-11-26
    • 1970-01-01
    • 1970-01-01
    • 2015-12-11
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多