请帮我理解haskell中的模式匹配。我有点困惑

时间:2010-01-20 13:47:02

标签: haskell list design-patterns match

如果我有类似的东西:

func (x1:x2:x3:xs) = xs

那么x1,x2,x3必须存在,是吗? 他们不能[],但必须(再次,必须)有一个价值,是吗? 另外,xs可以是[][a][a,a,a](等等),是吗?
(在[a]中我的意思是它是一个带有一个数字的列表,[a,a,a]是三个数字的列表。

我也有定义isPrefixOf的函数:

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
[]     `myIsPrefixOf`  []      = True
[]     `myIsPrefixOf`  (x:xs)  = True
list   `myIsPrefixOf`  []      = False
(l:ls) `myIsPrefixOf`  (x:xs)  = if l == x then ls `myIsPrefixOf` xs
                                 else False

如果我删除第一个模式,该函数将如下所示:

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
[]     `myIsPrefixOf`  (x:xs)  = True
list   `myIsPrefixOf`  []      = False
(l:ls) `myIsPrefixOf`  (x:xs)  = if l == x then ls `myIsPrefixOf` xs
                                 else False

现在我要写:

[] `myIsPrefixOf` [] 

我会得到:假(它应该是真的) 是因为第一个模式在他的右侧元素中有(x:xs),因此,x必须带有一个值,因此我通过第一个模式,然后进入第二个模式:

list   `myIsPrefixOf`  []      = False

匹配,并返回False 我是对的吗?

如果我是对的,那么区别在于如果我写(x:xs)x必须是一个值而不是[]
另一方面,如果我写list,它可以匹配[][a]以及[a,a,a](等等),因此,{{1}第二个模式,将匹配我输入中的第一个list,因此我会得到假? (和以前一样,[]我的意思是它是一个带有一个数字的列表,而[a]是三个数字的列表)。

另外,为了纠正这种情况,我需要更换:

[a,a,a]

[]     myIsPrefixOf  (x:xs)  = True

现在是表达式:

myIsPrefixOf

将再次匹配:

[]     `myIsPrefixOf`  list  = True

希望我对这些事情是正确的,现在又提出另一个问题:
这是从一开始的固定功能(应用更改后)

[] `myIsPrefixOf` []
[] `myIsPrefixOf` [1,2,3]

现在,如果我删除了第二个模式匹配,那么该函数将如下所示:

 [] `myIsPrefixOf` list = True

并调用函数:

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
[]     `myIsPrefixOf`  list  = True
list   `myIsPrefixOf`  []      = False
(l:ls) `myIsPrefixOf`  (x:xs)  = if l == x then ls `myIsPrefixOf` xs
                                 else False

我得到一个错误,表示该功能没有详尽的模式 我想看看我是否理解为什么会这样 该函数通过第一个模式并进入第二个模式:

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
[]     `myIsPrefixOf`  list  = True
(l:ls) `myIsPrefixOf`  (x:xs)  = if l == x then ls `myIsPrefixOf` xs
                                 else False

这样:

[1,2] `myIsPrefixOf` [1]

和:
(l:ls) `myIsPrefixOf` (x:xs) = if l == x then ls `myIsPrefixOf` xs else False
他们都是[1,2] `myIsPrefixOf` [1],所以我再次匹配第二种模式:

l == x

现在,1,但是(2:[]) `myIsPrefixOf` ([]:[]) 因此,表达式l == 2返回无穷尽的模式......
是因为我试图检查数字和列表之间的相等性吗? 等参数(==)应该只检查相同类型的元素?
(即:x == []l == x

好吧,我明白了吗? :-)
非常感谢: - )。

3 个答案:

答案 0 :(得分:4)

对于学习Haskell的人来说,这似乎是一种常见的误解。 :构造不是列表连接。因此,x:xs不匹配“名为x的事物列表,后跟名为xs的事物列表”。相反,请将:视为名为StartsAListThatContinues

同样,[]构造并不意味着“我不关心”或“某些列表,无论如何”。可以把它想象成NoMore

现在,想象一下您的原始代码是:

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
NoMore                            `myIsPrefixOf`  NoMore                             = True
NoMore                            `myIsPrefixOf`  (x `StartsAListThatContinues` xs)  = True
list                              `myIsPrefixOf`  NoMore                             = False
(l `StartsAListThatContinues` ls) `myIsPrefixOf`  (x `StartsAListThatContinues` xs)  = if l == x then ls `myIsPrefixOf` xs

最后,要意识到列表可以是NoMoreStartsAListThatContinues结构。一个或另一个,就是这样。

在这些条件下,也许很清楚你的代码是如何减少的(记住_意味着“我不关心”):

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
NoMore                            `myIsPrefixOf`  _                                  = True
list                              `myIsPrefixOf`  NoMore                             = False
(l `StartsAListThatContinues` ls) `myIsPrefixOf`  (x `StartsAListThatContinues` xs)  = if l == x then ls `myIsPrefixOf` xs

然后

myIsPrefixOf :: (Eq a) => [a] -> [a] -> Bool
NoMore                            `myIsPrefixOf`  _                                  = True
_                                 `myIsPrefixOf`  NoMore                             = False
(l `StartsAListThatContinues` ls) `myIsPrefixOf`  (x `StartsAListThatContinues` xs)  = if l == x then ls `myIsPrefixOf` xs

答案 1 :(得分:4)

您对第一个问题的理解是正确的,但您对第二个问题的理解不是。

要了解原因,请从实际功能中退一步并查看列表。列表有两个构造函数[]:。因此,完整的模式匹配需要涵盖两种情况。

您的函数有两个列表参数,因此您需要涵盖2 * 2 == 4个案例。这将始终是一个带有两个列表参数的函数的情况;如果你保留一个组合,你会得到一些输入的“非详尽模式”错误。这些是您在第一个版本中的案例:

[] `f` [] = True
[] `f` (x:xs) = True
(l:ls) `f` [] = False
(l:ls) `f` (x:xs) = ...

当您在两个列表构造函数上避免模式匹配时,可以将两个案例折叠为一个。这就是你在第一个问题中所做的:

[] `f` list = True
...

这里忽略了第二个参数的细节 - 它与哪个列表构造函数无关。只要这两种情况的答案相同,就像这样就可以将它折叠起来,就像这种情况一样。


对于你的第二个问题,你想放弃第三个案例。避免“非详尽模式”错误的唯一方法是使第四种情况不那么具体:

(l:ls) `f` xlist = ...

但是你被卡住了,因为你不能再找到xlist的第一个元素了,因为你不知道它不是空的。您可以执行head xlist,但这会在空列表中崩溃。所以实际上你必须首先检查空列表:

(l:ls) `f` xlist = if null xlist then False 
                   else if l == head xlist then ls `myIsPrefixOf` tail xlist
                   else False

但是那种冗长的原始模式匹配更好。


您在第二个问题中出错的具体方法是手动执行isPrefixOf [1,2] [1]

  

该函数通过第一个模式并进入第二个模式:

(l:ls) `myIsPrefixOf` (x:xs) = if l == x then ls `myIsPrefixOf` xs
                               else False
     

这样:

[1,2] `myIsPrefixOf` [1]
     

l == x.

到目前为止很好。

  

他们都是1,所以我再次匹配第二种模式:

等一下,等一下,弄清楚这里的所有值。我们已经知道l==x==1。但也ls==[2]xs==[]

现在,当我们重复时,ls将不匹配第一个模式(它不是空的),但xs将与第二个模式不匹配(它是空的,(x:xs)需要一个:对象,而不是[])。因此,该功能以“非详尽模式”崩溃。

答案 2 :(得分:1)

您的理解大多是正确的,但您似乎确实遇到了一些问题。如果您有一个列表list,表示您与x:xs匹配,那么listxs都是列表类型,但x是列表的元素类型。因此x除非您有列表列表,否则[]不能等于[1,2] `myIsPrefixOf` [1]

所以,在你的第二个例子中,在通话中

1

匹配[2] `myIsPrefixOf` [] 后的递归调用是

[]:[]

(也就是说,右侧不是[[]],它与{{1}}相同,一个元素列表,其中唯一的元素是空列表)并且您没有与第一个参数匹配的模式非空,第二个参数为空。