Haskell重用模式

Emi*_*man 9 haskell pattern-matching

在下面的代码中,三个不同的函数使用相同的模式匹配(节点n左右).如果我想添加一个模式,例如(Node n(Leaf)(Leaf))或更改我的数据类型,我必须更改所有函数.有没有办法重用这些模式,所以我只需要定义一次?

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
Run Code Online (Sandbox Code Playgroud)

我试过了

matchNode = (PNode n left right)
add matchNode x = Node (n+x) (add left x) (add right x) 
Run Code Online (Sandbox Code Playgroud)

但它不允许我使用模式_,我无法从中提取n,.

chi*_*chi 4

这仅回答了部分:

\n\n
\n

如果我想[...]更改我的数据类型,我必须更改所有函数。

\n
\n\n

在这种情况下,您可以通过\n使用模式同义词扩展定义自定义模式来避免更改所有函数

\n\n
{-# LANGUAGE PatternSynonyms #-}\n\n-- The old type:\n-- data Tree = Node Int Tree Tree\n--           | Leaf \n\n-- The new type\ndata Tree = NewNode Tree Int Tree  -- changed name & argument order\n          | Leaf\n\npattern Node n left right = NewNode left n right\n\nadd :: Tree -> Int -> Tree\nadd (Node n left right) x = Node (n+x) (add left x) (add right x) \nadd (Leaf) x = Leaf\n\n-- etc.\n
Run Code Online (Sandbox Code Playgroud)\n\n

上面,我将旧的构造函数重命名NodeNewNode,还更改了其参数的顺序。然后,我定义了一个pattern Node为兼容性桥,使下面的旧模式再次起作用。

\n\n
\n\n

问题还问:

\n\n
\n

有没有办法重用这些模式,这样我只需定义它们一次?

\n
\n\n

@PyRulez 评论了使用记录通配符来缩短模式。这是一个可能的方法:

\n\n
{-# LANGUAGE ViewPatterns, RecordWildCards #-}\n\n-- an auxiliary record type to define the custom pattern \ndata R = RNode { left::Tree , n::Int, right::Tree } | RLeaf\n-- a function to convert a Tree to a R\ntoR :: Tree -> R\ntoR (NewNode l n r) = RNode l n r\ntoR _ = RLeaf\n\n-- Here we use a shorter pattern:\nadd2 :: Tree -> Int -> Tree\nadd2 (toR -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)\n  -- ^^^^^^^^^^^^^^^^^^^ this brings in scope left, n, right\nadd2 (toR -> RLeaf) x = Leaf\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种特殊情况下,空间并没有获得巨大的增益。然而,无论模式有多大,在记录定义(和辅助函数)之后我们只需要编写toR -> RNode{...}.

\n\n

不过,我不确定我是否真的喜欢这个。

\n\n

首先,该记录用三个(部分!)函数污染了全局名称空间left :: R -> Tree, n :: R -> Int, right :: R -> Treen如果您尝试使用 eg作为另一个不相关函数 ( ) 的参数,这将触发一些警告The local name shadows the global name n

\n\n

其次,记录通配符扩展引入了一些未在代码中编写的变量——读者必须知道(或猜测)这些变量是什么。另请注意,该RNode{..}模式将 例如 引入n与全局名称不同类型的范围:Int而不是R -> Int。读者可能会认为这n是全局的,甚至在类型级别上也会被误导。

\n\n

第三,上面的代码使用视图模式,这些模式目前使详尽性检查器感到困惑:

\n\n
Patterns.hs:23:1: Warning:\n    Pattern match(es) are non-exhaustive\n    In an equation for \xe2\x80\x98add2\xe2\x80\x99: Patterns not matched: _ _\n
Run Code Online (Sandbox Code Playgroud)\n\n

在这种具体情况下,我们可以简单地写

\n\n
add2 :: Tree -> Int -> Tree\nadd2 (node -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)\nadd2 _ x = Leaf\n
Run Code Online (Sandbox Code Playgroud)\n\n

以避免警告,但一般来说我们并不总是有这种选择。

\n