正如我们从上一个问题中学到的那样,+=
运算符允许一次添加一个元素。是否可以“检测”以前添加的元素并控制将来如何添加?
这是一个开始调查的简单程序:
module Main (main) where
import Control.Monad (void)
import Text.XML.HXT.Core
main :: IO ()
main = void $ runX $ root [] [foo]
>>> writeDocument [withIndent yes] "test.xml"
foo :: ArrowXml a => a XmlTree XmlTree
foo = eelem "foo" += bar += bar += bar -- += is left associative
bar :: ArrowXml a => a XmlTree XmlTree
bar = ifA (deep (hasName "bar")) (eelem "baz") (eelem "bar")
在这里,foo
创建“ foo”节点及其内容。内容是用bar
箭头生成的,它应该足够聪明以检测先前添加的“ bar”元素并更改其行为。这里我们使用deep
简单:如果'bar'元素是'foo'元素的子元素,则无论它有多深,都应将其检测出来(也getChildren >>> hasName "bar"
应做一些技巧)。
因此,test.xml
文件的预期内容为:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar/>
<baz/>
<baz/>
</foo>
当然,它不起作用。这是我得到的:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar/>
<bar/>
<bar/>
</foo>
我的问题:
为什么bar
箭头无法检测到“ bar”元素?
如何检测?
这是类型签名真正有用的情况之一。凝视一下类型签名:
(+=) :: (ArrowXml a) => a b XmlTree -> a b XmlTree -> a b XmlTree
首先ArrowXml
是的子类Arrow
,它描述了某种将一些输入输入到某些输出的机器。您可以把它想象成一个带有传送带的大工厂,将传送带运送到不同的机器上,我们正在建造这些工厂机器,从而建造具有功能的工厂。例如,箭头组合器中的三个是:
(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c') |infixr 3|
Fanout: send the input to both argument arrows and combine their output.
arr :: (Arrow a) => (b -> c) -> a b c
Lift a function to an arrow.
(.) :: (Category cat) => cat b c -> cat a b -> cat a c
morphism composition.
现在,请仔细查看小写字母(类型变量):
(+=) :: (ArrowXml a) => a b XmlTree -> a b XmlTree -> a b XmlTree
显然,我们要使用两台将b
s转换为XmlTree
s的机器,并将它们“合并到一起”到一台使用b
in并推出的机器XmlTree
。但重要的是,此类型签名告诉我们或多或少可以实现此目的的唯一方法是:
arr1 += arr2 = arr f . (arr1 &&& arr2) where
f :: (XmlTree, XmlTree) -> XmlTree
f = _
这是因为“自由定理”;如果你不知道参数的类型,那么我们就可以证明你真的不能做太多吧。(这可能会稍微复杂一点,因为箭头可能具有未被完全封装的结构arr
,例如内部计数器,这些计数器在使用时会加在一起,.
然后设置为。因此,实际替换为泛型后,您可以去。)0
arr
arr f
a (XmlTree, XmlTree) XmlTree
因此,我们必须在此处并行执行两个箭头。这就是我要说的。因为组合(+=)
器不知道是什么b
,所以别无选择,只能将bb
并行地顺畅地馈入箭头,然后尝试将其输出组合在一起。所以,deep (hasName "bar")
不看foo
。
deep (hasName "bar") . foo
如果您确实愿意,可以与之建立一个相互递归的解决方案,但是这似乎有潜在的危险(即无限循环),因此简单地定义如下内容可能会更安全:
a ++= b = a += (b . a)
其中“当前” a被“馈送”到b以产生更新。为此,您必须从中导入.
,Control.Category
因为它与导入不同Prelude..
(仅用于合成)。看起来像:
import Prelude hiding ((.))
import Control.Category ((.))
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句