【问题标题】:Haskell recursion in lists列表中的 Haskell 递归
【发布时间】:2015-10-26 20:55:35
【问题描述】:

我有这样的代码:

appd (a:e) ((c,b):bs) | a == c && bs /= [] = b:appd(a:e) bs
                      | a /= c && bs /= [] = appd (a:e) bs
                      | a /= c && bs == [] = appd e ((c,b):bs)
                      | otherwise          = b:appd e ((c,b):bs)

它遍历两个列表,如[1,2,3][(1,2),(6,5),(3,5)],并获取第一个列表的第一个元素并将其与另一个列表中每个元组的第一个元素进行比较,如果它们相等,则保存该元组的第二个元素。它工作正常,但如果我取第一个列表的第二个元素,则比较不起作用,在本例中为2

例如,如果我有 [1,2,3][(1,2),(6,5),(3,5)] 之类的列表,则函数从第一个列表中获取 1 并与 1 进行比较,然后与 6 进行比较,然后与 3 进行比较。不采用第一个列表的第二个元素 - 2 并且不会再次进行比较。怎么了?

【问题讨论】:

  • 无法重现:在我的测试中,它确实返回了与123 配对的结果;例如,appd [1,2,3] [(1,2),(6,5),(3,5)] 似乎在崩溃之前正确返回了来自(1,2)2 和来自(3,5)5。如果我在最后添加(2,4),它会在崩溃之前返回4。然而,这个函数是非常不完整的——它最终在几乎每个输入上都会崩溃。也许如果你解决了这个问题(尝试检查 GHC 的输出并启用-fwarn-incomplete-patterns),它的行为会更像你想要的那样。
  • 回答您问题的方法是获取您已隔离的测试用例 (appd [1,2,3] [(1,2),(6,5),(3,5)]),并在您的头脑中、纸上或使用调试器仔细跟踪它的执行情况,然后思考很难。
  • 我试过了,但看不到错误
  • “保存这个元组的第二个元素”是什么意思?这里的“节省”是什么意思?保存是否意味着输出列表将包含该项目?对于[1,2,3][(1,2),(6,5),(3,5)]appd 是否应该返回[1,3]?输入列表是否必须具有相同的长度?元组的第二个元素是否曾经使用过?
  • 它从第一个列表中获取数字 1,并将每个元组的第一个元素与第二个列表进行比较,它比较 1 到 1、1 到 6、1 到 3,如果比较为真,则添加到结果元组的第二个元素,在这种情况下它将是 1。然后它从第一个列表中获取下一个元素并再次执行 copmarison - 2 到 1、2 到 6、2 到 3 .....

标签: list haskell recursion


【解决方案1】:

首先,让我注意到您应该包含您收到的错误消息。您还应该向我们展示一些示例输出和示例输入。


无论如何:您当前的 appd 不处理空列表,因此您需要首先为此添加案例:

appd _     []          = []
appd []    bs          = snd <$> bs  -- you can decide to use [] instead
appd (a:e) ((c,b):bs)
  | a == c && bs /= [] = b:appd(a:e) bs
  | a /= c && bs /= [] = appd (a:e) bs
  | a /= c && bs == [] = appd e ((c,b):bs)
  | otherwise          = b:appd e ((c,b):bs)

现在您的函数可以在您提供的输入上运行,但我不确定它是否会返回您想要的结果:

*Main> appd [1,2,3] [(1,2),(6,5),(3,5)]
[2,5,5]

此外,我已经稍微清理了您的代码,并使用显式类型签名对您的函数进行了注释:

appd :: (Eq a, Eq b) => [a] -> [(a,b)] -> [b]
appd []         bs      = snd <$> bs
appd _          []      = []
appd as@(a:ass) bs@((c,b):bss)
  | a == c && bss /= [] = b : appd as  bss
  | a /= c && bss /= [] =     appd as  bss
  | a /= c && bss == [] =     appd ass bs
  | otherwise           = b : appd ass bs

另外,您可以使用更简单的非递归实现来获得与上述相同的结果:

appd :: (Eq a, Eq b) => [a] -> [(a,b)] -> [b]
appd as bs = snd <$> filter (\(a,_) -> a `elem` as) bs

或者如果你喜欢point free(又名tacit):

appd :: (Eq a, Eq b) => [a] -> [(a,b)] -> [b]
appd as = (snd <$>) . filter ((`elem` as) . fst)

注意:&lt;$&gt;fmap 的别名,当用于列表时,其行为与 map 完全相同。

【讨论】:

  • 啊,是的,我忘了说&lt;$&gt; 做了什么。
  • 非常感谢!!不幸的是,即使经过改进,我的功能仍然无法正常工作,但您建议的更简单的实现工作正常。这是示例输入:[1,2,3,4][(1,5),(3,8),(5,5),(4,3),(2,9)] 输出:[5, 9,8,3]
  • 哦,抱歉,再次检查 - 更简单的版本也给出了不正确的输出
  • 它从第一个列表中逐个获取元素,然后将其与其他列表中的元组进行比较。它将第一个列表中的元素与每个元组的第一个元素进行比较,如果它们相等,则将元组中的第二个元素添加到结果中。在这种情况下,它从第一个列表中取 1 并将其与 1、3、5、4 和 2 => 1=1 进行比较,因此将 5 添加到结果中。然后它需要 2 并重复相同的动作
猜你喜欢
  • 2011-07-16
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 2018-02-20
  • 1970-01-01
  • 2013-01-17
  • 2015-03-10
  • 1970-01-01
相关资源
最近更新 更多