为什么函数定义中首选模式匹配?

时间:2016-02-05 19:29:39

标签: function haskell pattern-matching signature

我正在阅读learnyouahaskell的“learnyouahaskell”教程。它写着:

  

模式匹配也可用于元组。如果我们想做什么呢   在2D空间中采用两个向量的函数(在表单中   对)并将它们加在一起?为了将两个向量相加,我们补充说   他们的x组件分开,然后是他们的y组件   分别。如果我们不知道的话,我们将如何做到这一点   模式匹配:

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)  
addVectors a b = (fst a + fst b, snd a + snd b)  
     

嗯,这样可行,但有更好的方法。让我们修改一下   函数,以便它使用模式匹配。

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)  
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)  
     

我们去!好多了。请注意,这已经是一个包罗万象的了   图案。 addVectors(在两种情况下)的类型都是addVectors :: (Num a) => (a, a) -> (a, a) - > (a, a),因此我们保证会得到   两对作为参数。

我的问题是:如果两个定义产生相同的签名,为什么模式匹配首选首选方式?

3 个答案:

答案 0 :(得分:5)

我认为在这种情况下,模式匹配更直接地表达了您的意思。

在函数应用案例中,需要知道fstsnd做了什么,并从中推断出ab是元素被添加的元组。

addVectors a b = (fst a + fst b, snd a + snd b)

我们有sndfst函数来分解元组这一事实让人分心。

在模式匹配的情况下,它立即清楚输入是什么(一个元组,我们称之为x1y1和一个元组......等)以及它是如何被解构的。而且还可以立即清楚发生了什么,如何添加元素。

addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)

这几乎就像数学定义:

  

x 1 y 1 )+( x 2 y 2 ):=( x 1 + x 2 y 1 + y 2

直截了当,没有分心: - )

你可以在Haskell中写下这个:

(x₁, y₁) `addVector` (x₂, y₂) = (x₁ + x₂, y₁ + y₂)

答案 1 :(得分:3)

简而言之,人们需要构建和破坏价值观。

通过获取数据构造函数(可能是null-ary)并应用所需的参数来构造值。到目前为止,非常好。

随机示例(滥用GADTSyntax

data T where
  A :: Int -> T
  B :: T
  C :: String -> Bool -> T

销毁更复杂,因为需要获取类型T的值并获取有关1)构造函数用于制作此类值的信息,以及2)所述构造函数的参数是什么。

第1部分)可以通过函数完成:

whichConsT :: T -> Int -- returns 0,1,2 for A,B,C

第2部分)更棘手。可能的选择是使用投影

projA :: T -> Int
-- projB not needed
projC1 :: T -> String
projC2 :: T -> Bool

以便例如他们满足

projA (A n) = n
projC1 (C x y) = x
projC2 (C x y) = y

但是等等!投影的类型具有T -> ...形式,它承诺这些函数适用于T类型的所有值。所以我们可以

projA B = ??
projA (C x y) = ??
projC1 (A n) = ??

如何实施以上?没有办法产生合理的结果,所以最好的选择是触发运行时错误。

projA B = error "not an A!"
projA (C x y) = error "not an A!"
projC1 (A n) = error "not a C!"

然而,这给程序员带来了负担!现在,程序员有责任检查传递给投影的值是否具有正确的构造函数。这可以使用whichConsT完成。许多命令式程序员习惯于这种接口(测试和访问,例如迭代器中的Java hasNext(), next()),但这是因为大多数命令式语言没有更好的选择。

FP语言(以及现在的一些命令式语言)也允许模式匹配。使用它比预测具有以下优点:

  • 无需拆分信息:我们同时获得1)和2)
  • 无法使程序崩溃:我们从不使用可能崩溃的部分投影功能
  • 对程序员没有负担:以上的推论
  • 如果详尽检查者已开启,我们一定会处理所有可能的情况

现在,对于具有一个构造函数(元组,()newtype s)的类型,可以定义总投影,这些投影非常精细(例如{{1 }})。不过,许多人更喜欢坚持模式匹配,这也可以处理一般情况。

答案 2 :(得分:2)

正如 Carsten 在评论中提到的,这是一个基于意见的问题,但无论如何我要详细说明。

对2元组使用模式匹配并不是一个优势,但让我们考虑一些更大的数据结构,例如4元组。

addVectors :: (Num a) => (a, a, a, a) -> (a, a, a, a) -> (a, a, a, a)  
addVectors a b = -- some code that adds vectors

addVectors :: (Num a) => (a, a, a, a) -> (a, a, a, a) -> (a, a, a, a)  
addVectors (w1, x1, y1, z1) (w2, x2, y2, z2) = (w1 + w2, x1 + x2, y1 + y2, z1 + z2)

如果没有模式匹配,你必须编写从4元组中提取第一,第二,第三和第四个元素的函数,并在addVectors内使用它。通过模式匹配,编写addVectors的实现非常容易。

我相信在书中使用这样的例子可以更有效地传达信息。