在Haskell的模式匹配中是否特别处理了“List”?

时间:2011-11-03 09:21:29

标签: haskell pattern-matching

我是Haskell的新手,希望这个问题不傻。

我见过很多例子,当我有一个列表时,我能够匹配并将列表的“组合元素”绑定到单个变量:

listSizeDesc :: [a] -> String
listSizeDesc [] = "Emtpy"
listSizeDesc (x:xs) = "Something inside"

但是,我尝试做类似的事情:

foo :: Int -> String
foo 0 = "Zero"
foo (n - 1) = "next number is " ++ show n

它不起作用。

在我看来,(n-1)和(x:xs)都描述了如何“创建”参数并将“组件”绑定到参数。 List的匹配方式是为了便于递归而专门设计的吗?因为在我看来,这个匹配/参数绑定逻辑不适用于除(:)之外的其他函数。

5 个答案:

答案 0 :(得分:18)

您遇到的问题是模式匹配仅适用于数据构造函数。数据构造函数本质上非常简单;它只需要采用数据值并将它们组合在一起。例如,data Foo = Bar a b只需要两个数据并在Foo标签下将它们组合在一起。您在第一个示例中使用的(:)函数不仅仅是一个函数;它是一个数据构造函数。它通过将左参数添加到右参数来构造新列表。

现在,模式匹配仅仅与此过程相反。它解构了一种数据类型。当您在模式中编写(x:xs)时,您将提取构造函数最初拼接在一起的两个数据。因此,所有模式匹配都会提取构造函数先前拼接在一起的数据。

有一个例外:n + k个模式。在Haskell98中,您被允许使用形式(n + k)的模式。这是一种任意的例外,它最近被删除了。如果您愿意,如果包含NPlusKPatterns语言编译指示,仍可以使用它。

答案 1 :(得分:14)

列表类型是带有构造函数的“Sum type”,类似于:

data List a =
     cons a (List a)
   | nil

您的第一个示例是数据类型的模式匹配(:的语法糖)。

您的第二个示例是整数上的模式匹配,它不是数据类型定义。在整数上,没有使用语法的模式。你可以用:

写下你的例子
foo :: Int -> String
foo 0 = "Zero"
foo n = "next number is " ++ show (n+1)

如果使用数据类型编码整数,请注意:

data Nat = Zero | Succ Nat deriving (Show)

然后您可以根据需要使用模式匹配。

foo :: Nat -> String
foo Zero = "Zero"
foo n@Succ(p) = "next number is " ++ show(n)

此处模式Succ(p)扮演n-1的角色。

答案 2 :(得分:5)

已经有一些很好的答案了,所以我不会理会主要问题。这是最佳使用它,但您尝试做的事情可以通过view patterns完成。

{-# LANGUAGE ViewPatterns #-}

foo :: Int -> String
foo 0 = "Zero"
foo (pred -> n) = "Next number is " ++ show n

答案 3 :(得分:4)

尽可能简单地说:
列表字面上是一系列连接。数字可以等同于算术运算的结果。不同之处在于a : b的结果只是a : b


更详细:

列表和(:)根本不是特例。让我们自己做:

data List2 a = End               -- equivalent of "[]"
             | Cat a (List2 a)   -- non-infix ":"
  deriving (Show)

所以[1, 2, 3],即= (1 : (2 : (3 : []))),将被写为:

a = Cat 1 (Cat 2 (Cat 3 End))

就像模式匹配(x:xs)一样,我们可以匹配List2:

newTail End = End
newTail (Cat _ x) = x

测试它:

*Main> tail [1,2,3]
[2,3]
*Main> newTail a
Cat 2 (Cat 3 End)

答案 4 :(得分:3)

moo :: Int -> String
moo 0 = "Zero"
moo n = "next number is " ++ show (n + 1)

n - 1是普通的函数应用程序,而不是模式。曾经为+做了一个例外,这可能是你要去的模型。你可以写点像

goo :: Int -> String
goo 0 = "Zero"
goo (n+1)  = "previous number is " ++ show n
<{1>}中的

;如果包含编译指示

,您仍然可以使用hugs执行此操作
ghc