列出递归调用中的列表列表

时间:2018-04-01 17:20:19

标签: list haskell recursion

这是我的代码:

findY mnozP a = 
    if null(Set.toList $ mnozP)==False
        then (
            if (((null(intersect ( Set.toList $ head $ Set.toList $ mnozP) (a))==False) && (null(( Set.toList $ head $ Set.toList $ mnozP) \\ (a))==False)))
                then (((intersect ( Set.toList $ head $ Set.toList $ mnozP) (a)) ++ (( Set.toList $ head $ Set.toList $ mnozP) \\ (a))) ++ findY ( Set.fromList $ tail $ Set.toList $ mnozP) (a))
                else findY ( Set.fromList $ tail $ Set.toList $ mnozP) (a)
            )
        else []
findY _ a = []
findY _ _ = []

此函数检查列表mnozP是否为空,如果不是,则采用第一项并检查其与列表a的交集是否为空and其差异不为空。如果条件为真,则实现这两个操作并再次递归调用列表b的尾部。 我实际上有两个问题:

  1. 我们假设以下列表:a = ["3","4"]mnozP = [["1","6"],["2","4","3","5"]]。输出为["3","4","2","5"]是否有任何函数可以根据每次迭代将此列表拆分为列表列表?预期输出应该看起来像[["3","4"],["2","5"]]
  2. 编译GHCi时说:

    Warning:
    Pattern match(es) are overlapped
    In an equation for `findY':
        findY _ a = ...
        findY _ _ = ...
    

    我不确定我的模式有什么问题。我试图涵盖可能发生的所有可能情况。 任何想法都错了吗?

  3. 编辑:(简短摘要:)函数findY应返回列表列表。每个内部列表都与一个具体的递归迭代相关(如果条件为真)。

1 个答案:

答案 0 :(得分:2)

您正在寻找的功能是列表构造函数。在你的归纳案例中,你有

(((intersect ( Set.toList $ head $ Set.toList $ mnozP) (a)) ++ (( Set.toList $ head $ Set.toList $ mnozP) \\ (a))) ++ findY ( Set.fromList $ tail $ Set.toList $ mnozP) (a))

只是

as ++ bs ++ cs

其中cs是递归调用的结果。如果findY返回列表列表,那么整个表达式也必须如此。通过归纳假设,cs是一个列表列表。换句话说,exp ++ cs中左侧的表达式(即as ++ bs)也必须是列表。但事实并非如此! intersect\\返回平面列表,您将它们连接在一起,获得一个新的平面列表。

要解决该问题,您只需将左表达式包装在列表中

[as ++ bs] ++ cs

或使用列表构造函数

(as ++ bs) : cs

如果还有其他任何你可以从中获得的,那就是你可以发现自己已经以更惯用的方式编写了代码。我将尝试向您介绍我如何“解压缩”您的代码。

首先,我添加了与您的描述相符的类型签名。

findY :: Eq a => [[a]] -> [a] -> [[a]]

有趣的是,仅这一变化就会指出归纳案中的表达是不正确的。

然后,我摆脱了Set <-> []次转化。您可以要求输入设置,也可以只是普通列表,并确保没有重复。我采用了后一种方法:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY mnozP a = 
if null(mnozP)==False
    then (
        if (((null(intersect (head mnozP) (a))==False) && (null(( head mnozP) \\ (a))==False)))
            then (((intersect ( head mnozP) (a)) ++ (( head mnozP) \\ (a))) ++ findY ( tail mnozP) (a))
            else findY ( tail mnozP) (a)
        )
    else []
findY _ a = []
findY _ _ = []

它已经越来越清晰了。你可以摆脱()的负荷。在Haskell中,函数应用程序只是空格字符。因此,不要像在许多语言中那样编写f(x),而是编写f x并用括号括起来仅消除歧义表达式:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY mnozP a = 
if (null mnozP) == False
    then
        if (null (intersect (head mnozP) a) == False) && (null (head mnozP \\ a) == False)
            then (intersect (head mnozP) a) ++ (head mnozP \\ a) ++ findY (tail mnozP) a
            else findY (tail mnozP) a
    else []
findY _ a = []
findY _ _ = []

接下来,请注意headtail的多次重复。您可以使用let表达式将这些绑定到变量,但有更好的方法。 Haskell允许您对参数进行模式匹配,以便根据输入选择应评估的分支。在这里,我们只需要知道列表是否有头部和尾部,这恰好是模式匹配非空列表的方式:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY (xs : xss) a = 
    if (null (intersect xs a) == False) && (null (xs \\ a) == False)
        then (intersect xs a) ++ (xs \\ a) ++ findY xss a
        else findY xss a
findY _ _ = []

看看这样做是如何让我摆脱第一个if声明的? (我也删除了额外的模式匹配)。还有一些冗余,让我们摆脱它:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY (xs : xss) a =
  let as = intersect xs a
      bs = xs \\ a
  in
    if (null as == False) && (null bs == False)
        then as ++ bs ++ findY xss a
        else findY xss a
findY _ _ = []

此时,问题突出。

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY (xs : xss) a =
  let as = intersect xs a
      bs = xs \\ a
  in
    if (null as == False) && (null bs == False)
        then (as ++ bs) : findY xss a
        else findY xss a
findY _ _ = []

现在这是一个风格问题,但我个人觉得使用模式匹配比大多数时候的if语句更优雅。不过这里没有那么多:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY (xs : xss) a =
  let as = intersect xs a
      bs = xs \\ a
  in case (as, bs) of
    ((_ : _), (_ : _)) -> (as ++ bs) : findY xss a
    _                  -> findY xss a
findY _ _ = []

但是在Haskell 2010中,您可以使用模式保护,这是一种更强大的模式匹配版本。它使您的代码更加清晰:

findY :: Eq a => [[a]] -> [a] -> [[a]]
findY (xs : xss) a 
  | as@(_ : _) <- intersect xs a
  , bs@(_ : _) <- xs \\ a = (as ++ bs) : findY xss a
  | otherwise             = findY xss a
findY _ _ = []

最后,程序的形状是经典的结构递归。通常使用foldr作为。

findY :: Eq a => [a] -> [[a]] -> [[a]]
findY a = foldr build []
  where 
    build xs yss
      | as@(_ : _) <- intersect xs a
      , bs@(_ : _) <- xs \\ a = (as ++ bs) : yss
      | otherwise             = yss