它们在严格性上有所不同。假设我们给它们重新命名:
> let addVectorsStrict (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)
> let addVectorsLazy a b = (fst a + fst b, snd a + snd b)
addVectorsStrict undefined undefined
是 undefined
——模式匹配会在要求结果的外部 (,)
构造函数被执行时立即进行:
> 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
fst,snd
的替代方案是可以的,但是在 Stack Overflow 上我见过太多初学者使用危险函数,比如head,tail,fromJust
,这些函数经常会导致崩溃,而模式匹配则更加安全,特别是当使用-Wall
警告(强烈推荐)时。 - chi