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,左和右.
这仅回答了部分:
\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.\nRun Code Online (Sandbox Code Playgroud)\n\n上面,我将旧的构造函数重命名Node为NewNode,还更改了其参数的顺序。然后,我定义了一个pattern Node为兼容性桥,使下面的旧模式再次起作用。
问题还问:
\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\nRun Code Online (Sandbox Code Playgroud)\n\n在这种特殊情况下,空间并没有获得巨大的增益。然而,无论模式有多大,在记录定义(和辅助函数)之后我们只需要编写toR -> RNode{...}.
不过,我不确定我是否真的喜欢这个。
\n\n首先,该记录用三个(部分!)函数污染了全局名称空间left :: R -> Tree, n :: R -> Int, right :: R -> Tree。n如果您尝试使用 eg作为另一个不相关函数 ( ) 的参数,这将触发一些警告The local name shadows the global name n。
其次,记录通配符扩展引入了一些未在代码中编写的变量——读者必须知道(或猜测)这些变量是什么。另请注意,该RNode{..}模式将 例如 引入n与全局名称不同类型的范围:Int而不是R -> Int。读者可能会认为这n是全局的,甚至在类型级别上也会被误导。
第三,上面的代码使用视图模式,这些模式目前使详尽性检查器感到困惑:
\n\nPatterns.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: _ _\nRun Code Online (Sandbox Code Playgroud)\n\n在这种具体情况下,我们可以简单地写
\n\nadd2 :: Tree -> Int -> Tree\nadd2 (node -> RNode{..}) x = Node (n+x) (add2 left x) (add2 right x)\nadd2 _ x = Leaf\nRun Code Online (Sandbox Code Playgroud)\n\n以避免警告,但一般来说我们并不总是有这种选择。
\n