non*_*ont 1 haskell tree-traversal
我试图了解如何使用 Traversable 迭代我的树数据结构。我有一棵丑陋的树(实际上是森林),看起来像这样:
data ParseTree a = Leaf a | Node a (ParseTree a) (ParseTree a) | MultiNode a [ParseTree a]
deriving (Show, Functor, F.Foldable, T.Traversable)
t = Node S
(Node NP (Leaf DP) (Leaf NP))
(MultiNode VP
[Node VP (Node VP (Leaf VP) (Leaf PP)) (Node NP (Leaf DP) (Leaf NP)),
Node VP (Leaf VP) (Node PP (Leaf PP) (Node NP (Leaf DP) (Leaf NP)))]
)
Run Code Online (Sandbox Code Playgroud)
我想找到多节点,以便我可以替换构建新树,多节点中的每个项目一个。
对我来说编写这样的函数很容易
findmulti :: ParseTree a -> [ParseTree a]
findmulti (Leaf a) = []
findmulti (Node a left right) = findmulti left ++ findmulti right
findmulti (MultiNode a lst) = [ (MultiNode a lst) ]
Run Code Online (Sandbox Code Playgroud)
但我认为我应该能够使用 traverse 向下列表并为我找到项目。如果我做
traverse print t
Run Code Online (Sandbox Code Playgroud)
我只得到最后的值:
S NP DP NP VP VP VP VP PP...
但我实际上希望遍历在多节点处停止。如何控制遍历的深度?或者这是不可能的,因为 Traversable 和朋友对容器不可知?
最终,我想导出也允许我替换多节点的镜头,但现在我只是想了解 traversable 的工作原理。我是否使用了正确的工具来完成这项工作?
构建一个Traversal'访问任何你喜欢的东西是相当容易的,尽管这个功能比Data.Traversable允许的更通用,因为它 Traversal必须只访问包含的元素。
首先,让我们检查一下签名 traverse
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
Run Code Online (Sandbox Code Playgroud)
我喜欢将第一个参数称为“注入”函数。被注入的东西Applicative,f是由在“访问” Traversal。在这种情况下,我们想访问MultiNodes 以便我们可以专门研究这种类型。
multinodes :: Applicative f => (ParseTree a -> f (ParseTree a)) -> ParseTree a -> (f (ParseTree a))
Run Code Online (Sandbox Code Playgroud)
或者
multinodes :: Traversal' (ParseTree a) (ParseTree a)
Run Code Online (Sandbox Code Playgroud)
现在让我们考虑定义。同样,目标是注入每个MultiNode.
-- No multinodes here, so we use `pure` so as to inject nothing at all
multinodes inj l@Leaf{} = pure l
-- Here's a multinode, so we'll visit it with the injection function
multinodes inj m@Multinode{} = inj m
-- And we need to neither reject (thus stopping the traversal)
-- nor inject a branch. Instead, we continue the traversal deeper
multinodes inj (Node a l r) =
(\l' r' -> Node a l' r') <$> multinodes inj l
<*> multinodes inj r
Run Code Online (Sandbox Code Playgroud)
这是我们的Traversal'.
>>> t ^.. multinodes
[MultiNode VP [Node VP (Node VP (Leaf VP) (Leaf PP)) (Node NP (Leaf DP) (Leaf NP)),Node VP (Leaf VP) (Node PP (Leaf PP) (Node NP (Leaf DP) (Leaf NP)))]]
Run Code Online (Sandbox Code Playgroud)
这段代码并不比为其编写的代码短多少findmulti——事实上,它只不过是一个展开的findMulti . traverse,但它立即与其他lens组合器兼容,并演示了Applicative在针对所需内部结构的同时馈入类型的一般方法。只写Traversal'一次这将是几乎任何类型的访问的通用工具MultiNode。
| 归档时间: |
|
| 查看次数: |
720 次 |
| 最近记录: |