2015-07-03 20 views
9

在下面的代碼中,相同的模式匹配(節點n左側)由三個不同的函數使用。如果我想添加一個模式,例如(節點n(葉)(葉))或改變我的數據類型,我必須改變所有的功能。有沒有辦法重用這些模式,所以我只需要定義一次呢?Haskell複用模式

data Tree = Node Int Tree Tree 
      | Leaf 

add :: Tree -> Int -> Tree 
add (Node n left right) x = Node (n+x) (add left x) (add right x) 
add (Leaf) x = Leaf 

subtract :: Tree -> Int -> Tree 
subtract (Node n left right) x = Node (n-x) (subtract left x) (subtract right x) 
subtract (Leaf) x = Leaf 

swap :: Tree -> Tree 
swap (Node n left right) = Node n right left 
swap (Leaf) = Leaf 

我試圖

matchNode = (PNode n left right) 
add matchNode x = Node (n+x) (add left x) (add right x) 

,但它不會允許我使用模式_,我不能提取ñ離開,並從它的權利

+2

如果您使用GHC,則可以使用[記錄通配符](http://downloads.haskell.org/~ghc/latest/docs/html/users_guide/syntax-extns.html#record-wildcards)。 – PyRulez

回答

4

這只是回答了一部分:

如果我想[...]改變我的數據類型,我不得不改變所有的功能。

在這種情況下,你能避免受 定義與pattern synonyms extension一個自定義模式改變所有的功能:

{-# LANGUAGE PatternSynonyms #-} 

-- The old type: 
-- data Tree = Node Int Tree Tree 
--   | Leaf 

-- The new type 
data Tree = NewNode Tree Int Tree -- changed name & argument order 
      | Leaf 

pattern Node n left right = NewNode left n right 

add :: Tree -> Int -> Tree 
add (Node n left right) x = Node (n+x) (add left x) (add right x) 
add (Leaf) x = Leaf 

-- etc. 

上面,我改名爲舊構造NodeNewNode,也改變了順序的論點。然後,我將pattern Node定義爲兼容性橋樑,使舊模式再次起作用。


問題也問:

有沒有辦法再使用這些模式,所以我只需要定義一次?

@PyRulez評論使用記錄通配符縮短模式。這裏有一個可能的方法:

{-# LANGUAGE ViewPatterns, RecordWildCards #-} 

-- an auxiliary record type to define the custom pattern 
data R = RNode { left::Tree , n::Int, right::Tree } | RLeaf 
-- a function to convert a Tree to a R 
toR :: Tree -> R 
toR (NewNode l n r) = RNode l n r 
toR _ = RLeaf 

-- Here we use a shorter pattern: 
add2 :: Tree -> Int -> Tree 
add2 (toR -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x) 
    -- ^^^^^^^^^^^^^^^^^^^ this brings in scope left, n, right 
add2 (toR -> RLeaf) x = Leaf 

在這種特殊情況下,空間沒有太大的增益。但是,無論模式有多大,在記錄定義(和輔助功能)之後,我們只需編寫toR -> RNode{...}

雖然我不確定我是否真的喜歡這個。

首先,該記錄污染全局名稱空間,具有三個(部分!)函數left :: R -> Tree, n :: R -> Int, right :: R -> Tree。這會引發一些警告,如果您嘗試使用例如n作爲另一個無關函數的參數(The local name shadows the global name n)。其次,記錄通配符擴展引入了一些變量,這些變量不是寫在代碼中的 - 讀者必須知道(或猜測)這些變量是什麼。另外請注意,RNode{..}模式帶來n納入與全球名稱不同的類型:Int而不是R -> Int。讀者可能認爲n是全球性的,即使在類型層面也會被誤導。

三,上面的代碼使用視圖模式,目前這些混淆全面性檢查:

Patterns.hs:23:1: Warning: 
    Pattern match(es) are non-exhaustive 
    In an equation for ‘add2’: Patterns not matched: _ _ 

在這種特定的情況下,我們可以簡單地寫

add2 :: Tree -> Int -> Tree 
add2 (node -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x) 
add2 _ x = Leaf 

避免了警告,但一般來說,我們並不總是有這種選擇。

+0

糾正我,如果我錯了,但模式同義詞不允許你定義一個更加「簡潔」的同義詞'節點n左',並使用該同義詞來代替左節點中的'節點n左'模式的等式。正如PyRulez在他的評論中所建議的,記錄窗口似乎更接近OP所要查找的內容。 – Jubobs

+0

好的;我可以看到你所描述的在OP中是如何有用的。 – Jubobs

+1

@Jubobs我現在試着應用PyRulez的建議,所以這也應該回答「縮短」部分。 – chi