2011-08-02 33 views
14

假設我有一個Vector數據類型定義如下:Haskell:喜歡模式匹配還是成員訪問?

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) 

(道歉,如果我有任何術語不正確)。

+3

只是爲了完整性:還有還有圖案使用記錄字段匹配的另一種形式:'vecAddA(向量{X = VX,Y = VY,Z = VZ})(向量{X = WX,Y = wy,y = wz})= ...' – hvr

回答

12

我通常會使用模式匹配,尤其是因爲您使用了所有構造函數的參數,並且它們中沒有很多。此外,在這個例子中這不是一個問題,但考慮到以下幾點:

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

fun x = a x + 1 

如果你使用模式匹配做的美孚類型的工作,你是安全的;無法訪問不存在的成員。另一方面,如果使用訪問函數,某些操作(如在此調用fun (B "hi!"))將導致運行時錯誤。編輯:雖然當然很有可能忘記匹配某些構造函數,但模式匹配使得它非常明確,發生了什麼取決於使用的構造函數(您也可以告訴編譯器檢測並警告不完整的模式)而使用一個函數會提示更多的構造函數,IMO。

訪問器是最好的保存的情況下,當你只想得到一個或幾個構造函數的(可能很多)參數,並且你知道使用它們是安全的(沒有在錯誤的構造函數上使用訪問器的風險,如上例)

+1

另一方面,如果你有'數據Foo = A {f :: Int} | B {f :: Int}',那麼使用'f'函數寫出一行來提取任何一種情況下的值(假設兩種情況下的邏輯都是相同的),而不是將兩行寫入模式會更簡潔匹配這兩種情況。 –

+0

是的,這可能是一個像'data SomeData = TextData {length :: Int,text :: String}類型的好主意。 BinaryData {length :: Int,blob :: [Word8]}'),其中兩個構造函數參數共享類型和用途。 – valderman

+0

@丹Burton:在flip-flipside,如果使用相同的邏輯對於這樣的類型是明智的,考慮將其重寫爲'data FooCase = A | B'和'data Foo = Foo {fooCase :: FooCase,f :: Int}',這也給你一個簡單的方法來檢查A和B而不打擾其餘。 –

4

這是一種美學偏好,因爲它們在語義上是等價的。嗯,我想在一個天真的編譯器中,第一個會因爲函數調用而變慢,但我很難相信在現實生活中不會優化它。

儘管如此,只有三個元素在記錄中,因爲您無論如何都使用了三個元素,並且大概有些對他們的順序的重要性,我會使用第二個元素。第二個(儘管較弱)的觀點是,這種方式你使用的是排序和分解的順序,而不是順序和字段訪問的混合。

+3

一致認爲,兩者之間沒有功能上的差異,而且我也沒有(現階段)擔心表現。我更感興趣的是一個更有經驗的哈斯克勒是否會看到其中的一個,並認爲「這是一種奇怪的寫作方式」。 – stusmith

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) 

我覺得模式匹配勝出這種類型的大多數情況下。一些明顯的例外:

  • 你只需要訪問一個較大的記錄
  • 一個或兩個字段你要保持靈活性以後更改記錄,比如添加更多的字段(或更改!)。
+0

至於第二點,「你希望保持靈活性以便稍後改變記錄,比如添加更多的字段」,這也可以成爲堅持(位置)模式匹配的原因,因爲那麼你必須仔細檢查每一個模式匹配,以確保您不會忘記更新您的功能處理其他字段(例如,考慮手動編寫的'NFData'實例) – hvr

4

(警報,可能是錯誤的。我仍然是一個Haskell新手,但這是我的理解)

其他人沒有提到的一件事是模式匹配將在其參數中的功能「嚴格」。 (http://www.haskell.org/haskellwiki/Lazy_vs._non-strict)

要選擇使用哪一個模式,程序必須減少參數之前WHNF 調用函數,而使用record-語法存取器函數將在函數內評估參數

我真的不能給出任何具體的例子(仍然是一個新手),但這可能會產生性能影響,在遞歸的非嚴格函數中可能會產生大量的「thunk」。 (這就是說,對於像提取值這樣的簡單函數,應該沒有性能差異)。

(具體例子非常歡迎)

總之

f (Just x) = x 

實際上是(使用BangPatterns

f !jx = fromJust jx 

編輯:以上是嚴格的一個很好的例子,因爲兩者實際上都是嚴格的(f bottom = bottom),只是爲了說明我的表現IDE。

+0

+1您說得對。我甚至在第一次閱讀時就錯過了這些。我在答案中給出了一個更清晰的例子。 – hammar

2

由於kizzx2 pointed out,存在嚴格vecAddAvecAddB

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語義等價。

+0

正如你所猜測的,它們在原始代碼中實際上是嚴格的。我忽略了這一點,試圖簡化問題。 – stusmith