关于列表:请帮助我理解haskell中的模式匹配。我有点困惑

please help me understand the pattern match in haskell. i'm a little confused

如果我有这样的事情:

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

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

我也有定义 isPrefixOf:

的函数

1
2
3
4
5
6
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

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

1
2
3
4
5
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

现在我要写:

1
[] `myIsPrefixOf` []

我会得到:假(应该是真)。
是不是因为第一个模式在他的右侧元素中有:(x:xs),正因为如此,x 必须有一个值,因此我通过第一个模式,并到达第二个模式:

1
list   `myIsPrefixOf`  []      = False

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

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

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

1
[]     <wyn>myIsPrefixOf</wyn>  (x:xs)  = True

这样:

1
[]     `myIsPrefixOf`  list  = True

现在是表达式:

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

将再次匹配:

1
 [] `myIsPrefixOf` list = True

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

1
2
3
4
5
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

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

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

并像这样调用函数:

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

我收到一条错误消息,指出函数中没有详尽的模式。
我想看看我是否明白为什么会这样。
该函数通过第一个模式并到达第二个模式:

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

所以:

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

和:
l == x
他们都是 1,所以我再次匹配第二个模式:

1
(2:[]) `myIsPrefixOf` ([]:[])

现在,l == 2,但是 x == []
正因为如此,表达式: l == x 返回非穷举模式......
是因为我正在尝试检查数字和列表之间的相等性吗?
相等参数(==)应该只检查相同类型的元素吗?
(即:'a' == 'b'1 == 3)

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


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

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

您的函数有两个列表参数,因此您需要涵盖 2 * 2 == 4 情况。对于带有两个列表参数的函数,情况总是如此;如果您遗漏一个组合,您将收到某些输入的"非详尽模式"错误。这些是您在第一个版本中遇到的情况:

1
2
3
4
[] `f` [] = True
[] `f` (x:xs) = True
(l:ls) `f` [] = False
(l:ls) `f` (x:xs) = ...

当您避免在两个列表构造函数上进行模式匹配时,您可以将两种情况合并为一种。这就是您在第一个问题中所做的:

1
2
[] `f` list = True
...

这里忽略了第二个参数的细节——不管它是哪个列表构造函数。只要两种情况的答案相同,就可以像这样折叠它,在这种情况下就是这样。

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

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

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

1
2
3
(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].

the function pass through the first pattern and get to the second one:

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

so:

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

and:

1
l == x.

到目前为止还不错。

they both 1, so i match again the second pattern:

等等,等一下,找出这里的所有值。我们已经知道 l==x==1。还有 ls==[2]xs==[].

现在当我们递归时,ls 不会匹配第一个模式(它不是空的),但是 xs 不会匹配第二个模式(它是空的,并且 (x:xs) 需要一个 : 对象,不是 [])。因此,该函数因"非详尽模式"而崩溃。


对于学习 Haskell 的人来说,这似乎是一个常见的误解。 : 构造不是列表连接。因此,x:xs 不匹配"名为 x 的事物列表后跟名为 xs 的事物列表"。相反,将 : 想象成它被命名为 StartsAListThatContinues.

同样,[] 结构并不意味着"我不在乎"或"一些列表,随便什么"。把它想象成它被命名为 NoMore.

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

1
2
3
4
5
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 结构。一个或另一个,仅此而已。

在这些条件下,也许你的代码可以减少是很清楚的(记住 _ 的意思是"我不在乎"):

1
2
3
4
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

然后

1
2
3
4
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


您的理解大部分是正确的,但您似乎确实有一些问题。当你有一个列表,比如说 list,你与 x:xs 匹配,那么 listxs 都是列表类型,但 x 是列表的元素类型。因此,x 不可能等于 [],除非您有一个列表列表,而这些示例没有。

所以,在你的第二个例子中,在调用

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

匹配1s后的递归调用是

1
[2] `myIsPrefixOf` []

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