我是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的匹配方式是为了便于递归而专门设计的吗?因为在我看来,这个匹配/参数绑定逻辑不适用于除(:)之外的其他函数。
答案 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