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

时间:2016-06-25 23:10:41

标签: haskell design-patterns tree matching infinite

我正在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,_,_],依此类推。我该如何阻止它?

4 个答案:

答案 0 :(得分:4)

当您使用列入列表的函数时,通常需要使用递归和列表元素函数。

让我们来看看你到目前为止所做的事情。

在第四行,当您收到包含三个元素的列表时,您将构建评估结果:

this

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

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 head,{{1 }和tail

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

last

通过使用initbuild :: [LogMessage] -> MessageTree build [x] = Node Leaf x Leaf build l = Node (build (init l)) (last l) Leaf 处理常规列表元素,而不是使用列表中明确指定的元素,我们可以消除对无限模式匹配的需要。

答案 1 :(得分:2)

以正向计算,累积式重写代码:

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)模式?

答案 2 :(得分:1)

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

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

答案 3 :(得分:1)

你需要递归地编写这个函数,通过找到一种方法来根据一个更简单的情况解决一个复杂的案例。因为模式匹配是一个列表,所以您将使用(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