这个问题是关于HXT的,但我想它适用于概念
ArrowPlus
一般来说。考虑以下程序:
module Main (main) where
import Text.XML.HXT.Core
import Control.Monad (void)
main :: IO ()
main = void $ runX $ root [] [foo]
>>> writeDocument [withIndent yes] "test.xml"
foo :: ArrowXml a => a XmlTree XmlTree
foo = selem "foo" [bar >>> bar >>> bar]
bar :: ArrowXml a => a XmlTree XmlTree
bar = this <+> eelem "bar"
你能告诉 test.xml
中会保存什么吗? ?我的期望:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar/>
<bar/>
<bar/>
</foo>
我的逻辑:箭头bar
复制其所有输入并添加一个“bar”元素
( this
是恒等箭头的别名):
| |
this eelem "bar"
| |
\ /
\ /
<+>
|
所以,bar >>> bar >>> bar
的结果应该是三个“bar”元素(注意
那eelem "bar" >>> eelem "bar"
只会产生一个“bar”元素,
由于 mkelem
的箭头家人忽略他们的意见(尽管仍然可以
用于生成其内容)并仅输出新创建的元素)。
话虽如此,我现在展示test.xml
的内容执行后
程序:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<//>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
<bar/>
</foo>
问题:
什么是
<//>
?为什么有 7 个“bar”元素而不是 3 个?这是什么原因 重复?
为什么当我替换
bar >>> bar >>> bar
时与none >>> bar >>> bar >>> bar
我得到:
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
哪里none
是零箭头。我们在这里处理箭头上的幺半群,对吗?
none
(≡ zeroArrow
) 应该是它的恒等式,所以它应该像:
none <+> eelem "bar"
它产生一个“bar”元素和后续的
调用应该添加另外两个元素。但我们什么也没有得到!
- 如何编写
bar
的正确版本会添加一个“栏”的箭头 一次元素?
抱歉问了 4 个问题,但我猜它们是密切相关的,所以它 应该不是问题。
最佳答案
您似乎对 >>>
的方式有些困惑和<+>
运算符(operator)工作。为了建立直觉,我们首先定义两个不同的 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
在bar1
复制其元素。现在,让我们将它们加载到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/>
的每个节点与 bar1
再次,导致:
bar1 === <//><bar/>
bar1 >>> bar1 === <//><bar/><bar/><bar/>
|--------||----------|
First Second
现在你继续这样做,你就会看到如何 bar1 >>> bar1 >>> bar1
将生产七个 <bar/>
s 前面带有 <//>
.
好的,现在我们对 >>>
有直觉了就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/>"]
哦,这非常简单......他们只是一个接一个地附加。我们可以看到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
对我来说,它需要一个 b
类型的值并返回 c
类型的值。因为我们不知道什么c
是(我们没有将其作为 none
的参数提供),我们可以假设它将是空集。这与我们之前对 >>>
的定义是有道理的。和<+>
:
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>"]
关于xml - 一次添加一个元素的箭头,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30029001/