如何停止 Haskell 中的无限模式匹配?

How to stop infinite pattern-matching in Haskell?

我正在做 http://www.cis.upenn.edu/~cis194/spring13/lectures.html

作业 2 中的练习 3
build :: [LogMessage] -> MessageTree
build [x] = Node Leaf x Leaf
build [x,y] = Node (build [x]) y Leaf
build [x,y,z] = Node (Node (build [x]) y Leaf) z Leaf

我有一个问题,我不知道如何结束模式匹配。它只会继续扩展到 [x,y,z,_] 然后 [x,y,z,_,_] 等等。我该如何阻止它?

您可能喜欢 x:xs 模式,它匹配任何(非空)列表,将 x 绑定到第一个元素,将 xs 绑定到其余元素。它通常与递归定义结合使用,以依次检查列表的每个元素,无论列表有多长。

如果你想匹配比三个元素更长的列表,模式可以扩展到x:y:z:rest;在这种情况下,xyz 将绑定到列表的前三个元素,而 rest 将绑定到其余元素。

你需要递归地编写这个函数,方法是找到一种方法,使一个复杂的案例可以用一个更简单的案例来解决。因为您要进行模式匹配的是列表,所以您将使用 (x:xs) 模式将列表分解为头部和尾部。像这样:

build :: [LogMessage] -> MessageTree
build [] = ... -- remember lists can be empty!
build [x] = Node Leaf x Leaf
build (x:xs) = -- something to do with build [x] and xs

以前向计算、累积的方式重写您的代码:

build [x] = go Leaf [x]
  where
  go tree (x:xs) = go (Node tree x Leaf) xs  -- build up ("accumulate") the tree 
  go tree []     = tree                      -- and reduce the list until empty

build [x,y] = go (Node Leaf x Leaf) [y]      -- can we use another, simpler 
  where                                      --   initial accumulator value?
  go tree (x:xs) = go (Node tree x Leaf) xs
  go tree []     = tree

....

你现在能简化一下吗?你注意到这里的相似之处了吗?可以使用任意长度的列表调用 build,即匹配 (x:xs) 模式吗?

当您使用接受列表的函数时,您通常会希望使用递归和列表元素函数。

让我们看看你目前有什么。

在第四行,当它接收到一个包含三个元素的列表时,您已构建了以下评估:

build [x,y,z] = Node (Node (build [x]) y Leaf) z Leaf

在第三行,当它收到一个包含两个元素的列表时,您的构建评估如下:

build [x,y] = Node (build [x]) y Leaf

如果您查看这两行,您会注意到其中有一些重复。具体来说,两者都包含 Node (build [x]) y Leaf。当出现这样的重复时,递归可以提供帮助。

使用递归,您可以用包含两个元素的列表递归调用构建函数来替换重复。例如,您可以将第四行替换为:

build [x,y,z] = Node (build [x,y]) z Leaf

这样简单了一些,但还没有解决问题。

build :: [LogMessage] -> MessageTree
build [x] = Node Leaf x Leaf
build [x,y] = Node (build [x]) y Leaf
build [x,y,z] = Node (build [x,y]) z Leaf

如果您看一下新函数,您会发现我们正在使用的一些常量可以使用列表元素函数替换,例如 headtaillast, 和 init.

在函数的每个模式中,我们只单独处理列表的最后一个元素。然后将非最后一个元素传递回 build。因此,我们可以通过使用 last 来访问列表的最后一个元素,并使用 init 来获取除最后一个元素之外的所有元素,从而摆脱一些模式匹配。

build :: [LogMessage] -> MessageTree
build [x] = Node Leaf x Leaf
build l = Node (build (init l)) (last l) Leaf

通过使用 initlast 处理一般列表元素,而不是处理明确指定的列表元素,我们可以消除无限模式匹配的需要。