如何理解和区分模式匹配和元组?

时间:2016-02-23 03:15:35

标签: haskell

我是Haskell的新手,并且对模式匹配和元组感到困惑。例如,在Learn You a Haskell for Great Good!中:

  

您还可以使用绑定到模式匹配的位置!我们可以将前一个函数的where部分重写为:

...  
where bmi = weight / height ^ 2  
      (skinny, normal, fat) = (18.5, 25.0, 30.0)  

(skinny, normal, fat) = (18.5, 25.0, 30.0)是一种模式匹配,但它也是一个元组,对吧?我如何理解和区分模式匹配和元组?

3 个答案:

答案 0 :(得分:2)

模式匹配用于命名以特定模式设置的变量。 在这里,您可以说“变量skinny normalfat代表元组的值,它等于(18.5, 25.0, 30.0)

您可以在其他情况下使用模式匹配,例如列表。

first_element [] = error "Empty list"
first_element (x:rest) = x

此处的模式是空列表[]或值x,后跟给定列表的其余部分。

如果您输入GHCI

let (x:xs) = [2, 3, 4]

然后,模式匹配x的值为2,xs的值为[3, 4]

元组只是“盒子”,可以包含多种类型的多个值。 点可以表示为(Double, Double)类型的元组。

您可以构造任何类型的任何元组。在您的示例中,(18.5, 25.0, 30.0)可能属于(Double, Double, Double)

类型

答案 1 :(得分:1)

在Haskell中,模式和表达式在很大程度上共享相同的内部语法,区别在于它们出现的位置。所以在你的例子中:

where bmi = weight / height ^ 2  
      (skinny, normal, fat) = (18.5, 25.0, 30.0)  

... (skinny, normal, fat)是一种模式,因为它出现在等号的左侧上,(18.5, 25.0, 30.0)是一个表达式,因为它是一个表达式在等号的右侧

在Haskell中有一个明确的上下文列表,其中允许使用模式,因此模式是出现在这些上下文中的事物。第一个上下文:定义方程的左侧。这可以是顶级定义,也可以是wherelet内的本地定义:

 -- Top level definition
 (pat1, pat2) = ...
   where
     -- Local definition in `where`
     (pat3:pat4:_) = let 
                        -- Local definition in `let`
                        Maybe pat5 = ...
                     in ...

第二个上下文:->表达式中case的左侧:

case expr of
  Just pat -> 2*a
  Nothing -> 42

第三个上下文:<-do的左侧 - 符号或列表推导:

example1 = do
  (pat1, pat2) <- ...
  ...

example2 = [f x y | (x, y) <- ...]

我可能会错过其他一些背景,但至少这些是主要的背景。

这里的另一个重要概念是数据构造函数:Haskell中的一个常量或运算符,它提供两个函数:

  1. 作为表达式,可用于构建其类型的值;
  2. 作为模式,可用于解构其类型的值。
  3. 同样,数据构造函数的具体使用是否算作前者或后者,完全取决于它是否是左侧&#34;与#34;右侧&#34;使用它。

答案 2 :(得分:-1)

(以下内容并非严格正确;正如Shumush所指出的,模式匹配只能通过数据构造函数完成,而不仅仅是任何函数。下面提到的(:)Just是实际的数据构造函数;各种逗号函数似乎并不存在,所以我实际上并不知道模式与元组的匹配是如何工作的。或许可以将其视为元组模式匹配的巧合类比。)

首先,使用map的定义作为示例,考虑模式匹配如何应用于列表。

map :: (a -> b) -> [a] -> [b]
map f [] = []
map f (x:xs) = f x (map f xs)

在这里,您可以对列表进行模式匹配,因为(:)是一个从元素和另一个列表生成列表的函数。也就是说,(x:xs)可以匹配[1,2,3],将1绑定到x,将[2,3]绑定到xs,因为(:) 1 [2, 3] = 1 : [2,3] = [1,2,3]

(,)是一个产生元组的函数:

> :t (,)
(,) :: a -> b -> (a, b)

所以(,) 1 2 = (1, 2)。因此,您可以使用与(,)相同的方式与(:)进行模式匹配:

(skinny, normal, fat) = (18.5, 25.0, 30.0)

(Prelude提供了(,,) :: a -> b -> c -> (a, b, c)(,,,)的定义以及更多内容。)

将18.5绑定到skinny,将25.0绑定到normal,将30.0绑定到fat,因为

(,) 18.5 25.0 30.0 = (18.5, 25.0, 30.0)

将模式匹配视为功能评估的对立面。您可以“拆分”一个值来获取用于生成该值的操作数,而不是将函数应用于值以生成新值。

模式匹配可以通过任何函数调用来完成,这就是为什么你可以对诸如Maybe值之类的东西进行模式匹配

-- From the Functor instance of Maybe
fmap :: (a -> b) -> (Maybe a) -> (Maybe b)
fmap f Nothing = Nothing
fmap f (Just x) = f x

由于Just :: a -> Maybe a是一个函数(特别是数据构造函数),因此您可以通过将Maybe创建的Just中包含的值绑定到x来进行模式匹配。