Haskell,如何计算树的所有节点(模式匹配)
问题在标题中:) 我的代码:Haskell,如何计算树的所有节点(模式匹配),haskell,pattern-matching,Haskell,Pattern Matching,问题在标题中:) 我的代码: data tree a = Leaf a | Node (tree a) (tree a) treeo = Node((Node(Leaf 1)(Node (Leaf 10)(Leaf 11)))) (Node(Leaf 12)(Leaf 13)) -- count all leafs: lcounter(Leaf a) = 1 lcounter(Node a b)= lcounter a + lcounter b -- cou
data tree a = Leaf a | Node (tree a) (tree a)
treeo = Node((Node(Leaf 1)(Node (Leaf 10)(Leaf 11))))
(Node(Leaf 12)(Leaf 13))
-- count all leafs:
lcounter(Leaf a) = 1
lcounter(Node a b)= lcounter a + lcounter b
-- count all nodes?:
首先请注意,数据类型定义应以大写开头:
data Tree a = Leaf a | Node (Tree a) (Tree a)
要计算所有节点的数量,基本上已经是您所做的,在树数据构造函数上进行简单的模式匹配:
countNodes :: Tree a -> Int
countNodes (Leaf a) = 0
countNodes (Node left right) = 1 + countNodes left + countNodes right
以你的例子:
let tree = Node((Node (Leaf 1) (Node (Leaf 10) (Leaf 11))))(Node (Leaf 12) (Leaf 13))
countNodes tree -- 4
首先请注意,数据类型定义应以大写开头:
data Tree a = Leaf a | Node (Tree a) (Tree a)
要计算所有节点的数量,基本上已经是您所做的,在树数据构造函数上进行简单的模式匹配:
countNodes :: Tree a -> Int
countNodes (Leaf a) = 0
countNodes (Node left right) = 1 + countNodes left + countNodes right
以你的例子:
let tree = Node((Node (Leaf 1) (Node (Leaf 10) (Leaf 11))))(Node (Leaf 12) (Leaf 13))
countNodes tree -- 4
让机器来做这项工作。这两个计数函数都是一个更一般的概念,折叠的实例。我将编写一小部分代码来折叠一棵树,并使用它免费定义两个所需的计数函数
首先,我将稍微概括一下您的树
类型,在节点
s处内部标记为b
s,在叶
s处外部标记为a
s
data Tree a b = Leaf a | Node (Tree a b) b (Tree a b)
原始的树
类型相当于树a()
此版本的树
是一个名为的有用类的实例Bifoldable
泛化为使用两个类型参数处理数据类型
instance Bifoldable Tree where
bifoldMap f g (Leaf a) = f a
bifoldMap f g (Node l x r) = bifoldMap f g l `mappend` g x `mappend` bifoldMap f g r
bifoldMap::monoidm=>(a->m)->(b->m)->树a b->m
使用。它识别树中的所有a
s和b
s,将f
或g
应用于它们以获得m
,然后使用mappend
将结果结构压缩为单个值
这是我将要编写的唯一一段非平凡的代码。其他一切都只是操纵类型countLeaves
和countNodes
都会从bifoldMap
的牙膏管中挤出来。(有一些可以为您生成Bifoldable
的实例,因此您甚至不需要编写完成繁重工作的代码!)
每个Bifoldable
自动以两种方式成为可折叠的。将双折叠
转换为可折叠
,从而聚合其第二个参数
newtype WrappedBifunctor p a b = WrapBifunctor { unwrapBifunctor :: p a b }
instance Bifoldable p => Foldable (WrappedBifunctor p a) where
foldMap f = bifoldMap (const mempty) f . unwrapBifunctor
这已经足够让我们计算节点数了。计数节点与计数b
type参数的出现次数相同,这正是length
对任何Foldable
所做的。我们所要做的就是将树包装成可折叠的
-- i lied, this is nontrivial too - but it's already in the standard library
length :: Foldable t => t a -> Int
length = foldl' (\c _ -> c+1) 0
countNodes :: Tree a b -> Int
countNodes = length . WrapBifunctor
ghci> let myTree = Node (Node (Leaf 'a') () (Leaf 'b')) () (Node (Leaf 'c') () (Leaf 'd'))
ghci> countNodes myTree
3
数树叶怎么样?这次我们需要计算类型参数的出现次数WrappedBifunctor
在这里也有帮助:我们可以重新排列Tree
的参数,使a
成为第二个参数,然后将其包装起来-生成的Foldable
实例将计数a
s,而不是b
s。为此,我们需要切换其参数,就像flip
在值级别所做的那样:
newtype Flip p a b = Flip { runFlip :: p b a }
instance Bifoldable p => Bifoldable (Flip p) where
bifoldMap f g = bifoldMap g f . runFlip
现在我们可以免费计算假期:
countLeaves :: Tree a b -> Int
countLeaves = length . WrapBifunctor . Flip
ghci> countLeaves myTree
4
最后,您可以使用Bifoldable
类中的函数计算叶子和节点
bilength :: Bifoldable t => t a b -> Int
bilength = bifoldl' (\c _ -> c+1) (\c _ -> c+1) 0
countLeavesAndNodes :: Tree a b -> Int
countLeavesAndNodes = bilength
-- equivalent to, but more efficient than...
-- countLeavesAndNodes t = countLeaves t + countNodes t
哈斯克尔的哲学是一劳永逸地解决这样的问题,并将这些通用解决方案应用于具体案例。像Flip
和WrappedBifunctor
这样的新类型让我们来表示一般属性(比如“每个Bifunctor
在翻转参数后仍然是一个Bifunctor
),而不需要运行时开销。我们使用这些属性操纵类型类系统,编写代码来计算a
s和b
s
在继续学习Haskell的过程中,您将了解到当程序是一般模式的实例时如何发现。设置像上面的双折叠
实例这样的通用结构可以很快提高代码重用的效率。让机器来完成这项工作。这两个计数函数都是一个更一般的概念,折叠的实例。我将编写一小部分代码来折叠一棵树,并使用它免费定义两个所需的计数函数
首先,我将稍微概括一下您的树
类型,在节点
s处内部标记为b
s,在叶
s处外部标记为a
s
data Tree a b = Leaf a | Node (Tree a b) b (Tree a b)
原始的树
类型相当于树a()
此版本的树
是一个名为的有用类的实例Bifoldable
泛化为使用两个类型参数处理数据类型
instance Bifoldable Tree where
bifoldMap f g (Leaf a) = f a
bifoldMap f g (Node l x r) = bifoldMap f g l `mappend` g x `mappend` bifoldMap f g r
bifoldMap::monoidm=>(a->m)->(b->m)->树a b->m
使用。它识别树中的所有a
s和b
s,将f
或g
应用于它们以获得m
,然后使用mappend
将结果结构压缩为单个值
这是我将要编写的唯一一段非平凡的代码。其他一切都只是操纵类型countLeaves
和countNodes
都会从bifoldMap
的牙膏管中挤出来。(有一些可以为您生成Bifoldable
的实例,因此您甚至不需要编写完成繁重工作的代码!)
每个Bifoldable
自动以两种方式成为可折叠的。将双折叠
转换为可折叠
,从而聚合其第二个参数
newtype WrappedBifunctor p a b = WrapBifunctor { unwrapBifunctor :: p a b }
instance Bifoldable p => Foldable (WrappedBifunctor p a) where
foldMap f = bifoldMap (const mempty) f . unwrapBifunctor
这已经足够让我们计算节点数了。计数节点与计数b
type参数的出现次数相同,这正是length
对任何Foldable
所做的。我们所要做的就是将树包装成可折叠的
-- i lied, this is nontrivial too - but it's already in the standard library
length :: Foldable t => t a -> Int
length = foldl' (\c _ -> c+1) 0
countNodes :: Tree a b -> Int
countNodes = length . WrapBifunctor
ghci> let myTree = Node (Node (Leaf 'a') () (Leaf 'b')) () (Node (Leaf 'c') () (Leaf 'd'))
ghci> countNodes myTree
3
数树叶怎么样?这次我们需要计算a
类型的出现次数