Haskell:喜欢模式匹配还是成员访问?

时间:2011-08-02 13:50:15

标签: haskell coding-style

假设我的Vector数据类型定义如下:

data Vector = Vector { x :: Double
                     , y :: Double
                     , z :: Double
                     }

使用成员访问来定义函数是否更常见:

vecAddA v w
    = Vector (x v + x w)
             (y v + y w)
             (z v + z w)

或使用模式匹配:

vecAddB (Vector vx vy vz) (Vector wx wy wz)
    = Vector (vx + wx)
             (vy + wy)
             (vz + wz)

(如果我的任何术语不正确,请道歉。)

6 个答案:

答案 0 :(得分:12)

我通常会使用模式匹配,特别是因为你正在使用所有构造函数的参数,并且它们不是很多。此外,在此示例中,这不是问题,但请考虑以下事项:

data Foo = A {a :: Int} | B {b :: String}

fun x = a x + 1

如果您使用模式匹配来处理Foo类型,那么您就是安全的;无法访问不存在的成员。另一方面,如果使用访问器函数,则在此处调用fun (B "hi!")等一些操作将导致运行时错误。

编辑:虽然当然很可能忘记在某些构造函数上匹配,但模式匹配使得它非常明确,所发生的事情取决于使用的构造函数(您还可以告诉编译器检测并警告您不完整的模式)而函数的使用暗示任何构造函数都是如此,IMO。

最好保存访问器,以用于只想获得一个或几个构造函数(可能很多)参数的情况,并且您知道使用它们是安全的(没有在错误的构造函数上使用访问器的风险,如在示例中。)

答案 1 :(得分:7)

另一个小的“真实世界”论点:一般来说,拥有这样的短记录条目名称并不是一个好主意,因为xy之类的短名称通常最终被用于局部变量。

所以这里的“公平”比较是:

vecAddA v w 
  = Vector (vecX v + vecX w) (vecY v + vecY w) (vecZ v + vecZ w)
vecAddB (Vector vx vy vz) (Vector wx wy wz) 
  = Vector (vx + wx) (vy + wy) (vz + wz)

我认为模式匹配在这种类型的大多数情况下都胜出。一些值得注意的例外:

  • 您只需要访问(或更改!)较大记录中的一个或两个字段
  • 您希望保持灵活性以便稍后更改记录,例如添加更多字段。

答案 2 :(得分:4)

这是一种审美偏好,因为这两者在语义上是等价的。好吧,我想在一个天真的编译器中,由于函数调用,第一个会慢一些,但我很难相信在现实生活中不会被优化掉。

尽管如此,记录中只有三个元素,因为你无论如何都在使用全部三个元素,并且可能某些对他们的订单有意义,我会使用第二个。第二个(虽然较弱)的论点是,这样你就可以使用合成和分解的顺序,而不是订单和字段访问的混合。

答案 3 :(得分:4)

(提醒,可能是错的。我仍然是Haskell的新手,但这是我的理解)

其他人没有提到的一件事是模式匹配会使其参数中的函数“严格”。 (http://www.haskell.org/haskellwiki/Lazy_vs._non-strict)

要选择使用哪种模式,程序必须在调用函数之前减少WHNF 的参数,而使用record-syntax访问器函数将评估参数 inside 功能。

我无法给出任何具体的例子(仍然是一个新手)但是这可能会产生性能影响,其中大量的“thunk”可以在递归的,非严格的函数中构建。 (这意味着,对于像提取值这样的简单函数,应该没有性能差异。)

(非常欢迎具体例子)

简而言之

f (Just x) = x

实际上是(使用BangPatterns

f !jx = fromJust jx

编辑:上面的不是严格的一个很好的例子,因为两者实际上都是严格的定义(f bottom = bottom),只是为了说明我从性能方面的意思。

答案 4 :(得分:2)

作为kizzx2 pointed outvecAddAvecAddB

之间的严格程度存在细微差别
vecAddA ⊥ ⊥ = Vector ⊥ ⊥ ⊥
vecAddB ⊥ ⊥ = ⊥

要在使用模式匹配时获得相同的语义,必须使用无可辩驳的模式。

vecAddB' ~(Vector vx vy vz) ~(Vector wx wy wz)
    = Vector (vx + wx)
             (vy + wy)
             (vz + wz)

但是,在这种情况下,Vector的字段应该是严格的,以提高效率:

data Vector = Vector { x :: !Double
                     , y :: !Double
                     , z :: !Double
                     }

对于严格字段,vecAddAvecAddB在语义上是等效的。

答案 5 :(得分:0)

Hackage包vect通过允许匹配如f(Vec3 x y z)和索引来解决这两个问题:

get1 :: Vec3 -> Float
get1 v = _1 v

查找HasCoordinates类。

http://hackage.haskell.org/packages/archive/vect/0.4.7/doc/html/Data-Vect-Float-Base.html