Haskell模式匹配向量

3

我正在跟随一个有关Haskell的在线 教程。我们定义了一个用于添加由数字元组对表示的二维向量的函数。以下是明确的类型声明,确保两个输入都是二维向量。

addVectors :: (Num a) => (a, a) -> (a, a) -> (a, a)

我理解为什么以下函数定义使用模式匹配:它描述了输入数据应符合的模式。

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

为什么以下替代函数定义不使用模式匹配?(fst) 和 (snd) 能够工作是因为输入被明确声明为长度为2的元组。两个函数定义的区别是什么?
addVectors a b = (fst a + fst b, snd a + snd b)

3
这只是用不同的方式定义相同事物的不同风格而已。你具体有什么问题? - leftaroundabout
1
作为一般建议,我建议尽可能学习使用模式匹配,至少在初学者级别上。fst,snd的替代方案是可以的,但是在 Stack Overflow 上我见过太多初学者使用危险函数,比如 head,tail,fromJust,这些函数经常会导致崩溃,而模式匹配则更加安全,特别是当使用 -Wall 警告(强烈推荐)时。 - chi
1个回答

7

它们在严格性上有所不同。假设我们给它们重新命名:

> let addVectorsStrict (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
> let addVectorsLazy a b = (fst a + fst b, snd a + snd b)

addVectorsStrict undefined undefinedundefined——模式匹配会在要求结果的外部 (,) 构造函数被执行时立即进行:

> case addVectorsStrict (error "A") (error "B") of (_, _) -> ()
*** Exception: A

但是addVectorsLazy undefined undefined(undefined, undefined)——直到需要结果中的一个元素之一时,模式匹配才会被延迟。

> case addVectorsLazy (error "A") (error "B") of (_, _) -> ()
()

基本上,addVectorsLazy 总是返回一个元组,其中的元素可能尚未计算。而 addVectorsStrict 可能不会返回。您也可以使用惰性模式匹配来实现 addVectorsLazy 的效果:

> let addVectorsAlsoLazy ~(x1, y1) ~(x2, y2) = (x1 + x2, y1 + y2)
> case addVectorsAlsoLazy (error "A") (error "B") of (_, _) -> ()
()

为了更好地理解求值顺序,您可以使用 Debug.Trace.trace 进行观察:
addVectors
  (trace "first tuple evaluated"
    (trace "x1 evaluated" 1, trace "y1 evaluated" 2))
  (trace "second tuple evaluated"
    (trace "x2 evaluated" 3, trace "y2 evaluated" 4))

评估 Haskell 的基本要点是要记住,它是通过使用 case 和函数方程(这些解糖化为 case)来驱动模式匹配的。在这种情况下并不重要,但您可以懒惰地编写函数,以避免在其结果永远不需要时评估昂贵的计算,或者严格地避免 thunk 的开销,如果您知道某些结果将始终需要。一般来说,除非需要它们是惰性的,否则最好使数据结构的字段变为严格,并使函数变为惰性。在此,您可以创建一个严格对类型来表示您的向量。
data Vector a = Vector !a !a

1
这是正确的,但对于提问者或从谷歌搜索到这个问题的任何人来说,可能太微妙了,没有什么用处。 - amalloy
谢谢理解我的问题 - 我没有很好地表达它,因为我不懂Haskell!也许我应该跟随官方教程 - Zhengqun Koo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接