这是我的代码:
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
的尾部。
我实际上有两个问题:
a = ["3","4"]
和mnozP = [["1","6"],["2","4","3","5"]]
。输出为["3","4","2","5"]
。 是否有任何函数可以根据每次迭代将此列表拆分为列表列表?预期输出应该看起来像[["3","4"],["2","5"]]
。编译GHCi时说:
Warning:
Pattern match(es) are overlapped
In an equation for `findY':
findY _ a = ...
findY _ _ = ...
我不确定我的模式有什么问题。我试图涵盖可能发生的所有可能情况。 任何想法都错了吗?
编辑:(简短摘要:)函数findY应返回列表列表。每个内部列表都与一个具体的递归迭代相关(如果条件为真)。
答案 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 _ _ = []
接下来,请注意head
和tail
的多次重复。您可以使用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