Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Function 为什么在函数定义中首选模式匹配?_Function_Haskell_Pattern Matching_Signature - Fatal编程技术网

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

Function 为什么在函数定义中首选模式匹配?,function,haskell,pattern-matching,signature,Function,Haskell,Pattern Matching,Signature,我正在阅读《learnyouahaskell》教程。上面写着: 模式匹配也可以用于元组。如果我们想做点什么呢 在二维空间中获取两个向量的函数(形式为 并将它们相加?要将两个向量相加,我们需要相加 它们的x组件分别,然后是y组件 分别地如果我们不知道,我们会这样做的 模式匹配: addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a) addVectors a b = (fst a + fst b, snd a + snd b)

我正在阅读《learnyouahaskell》教程。上面写着:

模式匹配也可以用于元组。如果我们想做点什么呢 在二维空间中获取两个向量的函数(形式为 并将它们相加?要将两个向量相加,我们需要相加 它们的
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)
,因此我们保证 两对作为参数


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

正如评论中提到的卡斯滕一样,这是一个基于观点的问题,但还是让我详细说明一下

使用与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
的实现非常简单


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

简而言之,我们需要构造和解构值

值是通过采用数据构造函数(可能是空的)函数并应用所需的参数来构造的。到目前为止,一切顺利

随机示例(滥用GADTSyntax)

销毁更为复杂,因为需要获取类型为
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),可以定义完全正确的总投影(例如,
fst,snd
)。尽管如此,许多人还是喜欢坚持模式匹配,这也可以处理一般情况

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

在函数应用程序中,需要知道
fst
snd
做了什么,并从中推断出
a
b
是添加了元素的元组

addVectors a b = (fst a + fst b, snd a + snd b)
事实上,我们有
snd
fst
函数来分解元组,这让人分心

在模式匹配的情况下,很明显输入是什么(一个元组,其元素我们称为
x1
y1
以及元组…等等),以及它是如何解构的。同时也很清楚发生了什么,它们的元素是如何添加的

addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
这几乎与数学定义相似:

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

直截了当,没有干扰:-)

你可以用Haskell写下这句话:

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

比较一下,什么更容易阅读?这不是更好,更接近实际定义吗?您不必解析/理解
fst
snd
来理解函数…这并不意味着冒犯,但这只是一个征求意见的问题-永远不会有答案(或任何正确答案)-这就是我投票关闭的原因it@Carsten嗯,你可能对可读性有点看法。我读了之后想:但在更多的代码中也是如此。既然你提到你不必知道
fst
snd
,我可以看出这可能有一个可读性优势。如果你做一些细微的布局修改,比如
addVectors(x₁,Y₁) (十)₂,Y₂) = (十)₁+x₂, Y₁+Y₂)。而且,我不认为使用四个元组作为示例有多聪明,因为基本上没有人使用大于三的元组。正确传达消息的是类似于列表单元格上的模式。
(x₁, y₁) `addVector` (x₂, y₂) = (x₁ + x₂, y₁ + y₂)