您似乎对>>>
和<+>
运算符的工作方式有些困惑。为了建立直觉,我们首先定义两个不同bar
的 s:
bar1 :: ArrowXml a => a XmlTree XmlTree
bar1 = this <+> eelem "bar"
bar2 :: ArrowXml a => a n XmlTree
bar2 = eelem "bar"
我们首先注意到的是类型签名。bar1
输入类型为XmlTree
,这意味着它以某种方式修改现有树,而bar2
丢弃其参数。这是由于使用this
inbar1
复制其元素。现在,让我们加载这些内容ghci
以了解如何协同工作>>>
:<+>
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2
["<bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2 >>> bar2 >>> bar2
["<bar/>"]
嗯,这很奇怪,无论我们用多少次组合它,它都会不断创建相同的结构>>>
。发生这种情况是因为对于bar2
,我们每次转换它时都会丢弃树:记住它的类型签名是a n XmlTree
而不是a XmlTree XmlTree
. 让我们将其与以下内容进行比较bar1
:
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1
["<//><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> bar1
["<//><bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> bar1 >>> bar1
["<//><bar/><bar/><bar/><bar/><bar/><bar/><bar/>"]
哇,它正在成倍增长!为什么?好吧,每次你用 编写时>>>
,你都在使用前一个树,并为每个元素应用函数 application this <+> eelem "bar"
。第一次调用bar1
没有先前的树,因此this
成为根节点,您只需<bar/>
向其附加一个元素。但是,对于bar1 >>> bar1
,第一个bar1
将创建<//><bar/>
,第二个将再次组成<//><bar/>
with的每个节点bar1
,从而导致:
bar1 === <//><bar/>
bar1 >>> bar1 === <//><bar/><bar/><bar/>
|--------||----------|
First Second
现在你继续这样做,你可以看到bar1 >>> bar1 >>> bar1
将如何产生 7 <bar/>
s 前面有 a <//>
。
好的,现在我们有了直觉>>>
,ArrowXml
我们可以看到<+>
行为方式:
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2 <+> bar2 <+> bar2
["<bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 <+> bar1 <+> bar1
["<//><bar/><//><bar/><//><bar/>"]
哦,这很简单......他们只是一个接一个地附加。我们可以看到 of 的类型bar1 <+> bar1
仍然是转换类型值的类型a XmlTree XmlTree
,所以如果将它与>>>
. 看看你是否可以围绕这个输出思考:
Prelude Text.XML.HXT.Core> runX $ xshow $ (bar1 <+> bar1) >>> bar1
["<//><bar/><bar/><bar/><//><bar/><bar/><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar1 >>> (bar1 <+> bar1)
["<//><bar/><//><bar/><bar/><bar/><bar/><bar/>"]
要回答关于 的问题none
,请检查它的类型签名:
Prelude Text.XML.HXT.Core> :t none
none :: ArrowList a => a b c
对我来说,它需要一个 type 的值b
并返回一个 type 的值c
。由于我们不知道是什么c
(我们没有将它作为参数提供给none
),我们可以假设它是空集。>>>
这对于我们之前对and的定义是有意义的<+>
:
Prelude Text.XML.HXT.Core> runX $ xshow $ none <+> bar1
["<//><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> bar1
[""]
在第一种情况下,空文档附加另一个文档基本上是身份操作。在第二个中,none
不产生任何元素,所以当你用组合它时bar1
,没有要操作的元素,因此结果是空文档。事实上,由于 Haskell 是懒惰的,我们可以更加随意地处理我们的组合,none
因为我们知道它永远不会被评估:
Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> undefined
[""]
鉴于这些知识,您可能尝试做的是这样的事情:
Prelude Text.XML.HXT.Core> let bar = eelem "bar"
Prelude Text.XML.HXT.Core> runX $ xshow $ selem "foo" [bar <+> bar <+> bar]
["<foo><bar/><bar/><bar/></foo>"]
编辑
类似的解决方案是使用+=
运算符:
Prelude Text.XML.HXT.Core> let bars = replicate 3 (eelem "bar")
Prelude Text.XML.HXT.Core> runX $ xshow $ foldl (+=) (eelem "foo") bars
["<foo><bar/><bar/><bar/></foo>"]