一次添加一个元素的箭头
Arrow to add one element at a time
这个问题是关于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 个?这是什么原因
重复?
为什么当我用 none >>> bar >>> bar >>>
bar
替换 bar >>> bar >>> bar
时,我得到:
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
其中 none
是零箭头。我们在这里处理箭头上的幺半群,对吗?
none
(≡ zeroArrow
) 应该是它的身份,所以它应该是这样的:
none <+> eelem "bar"
产生一个‘bar’元素和后续
调用应该添加另外两个元素。但是我们什么也没有!
- 如何编写
bar
箭头的正确版本以添加一个“条”
一次元素?
抱歉问了 4 个问题,但我想它们是密切相关的,所以
应该不是问题。
您似乎对 >>>
和 <+>
运算符的工作方式有些混淆。为了建立直觉,让我们首先定义两个不同的 bar
s:
bar1 :: ArrowXml a => a XmlTree XmlTree
bar1 = this <+> eelem "bar"
bar2 :: ArrowXml a => a n XmlTree
bar2 = eelem "bar"
我们首先注意到的是类型签名。 bar1
的输入类型为 XmlTree
,这意味着它以某种方式修改了现有的树,而 bar2
则丢弃了它的参数。这是由于在 bar1
中使用 this
复制了它的元素。现在,让我们将它们加载到 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/>"]
哇,它呈指数级增长!为什么?好吧,每次使用 >>>
进行组合时,您都会采用前一棵树,并且对于应用函数应用程序 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/>
前面有一个 <//>
。
好的,现在我们已经根据 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
类型的值,returns 需要一个 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>"]
这个问题是关于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 个?这是什么原因 重复?
为什么当我用
none >>> bar >>> bar >>> bar
替换bar >>> bar >>> bar
时,我得到:
<?xml version="1.0" encoding="UTF-8"?>
<foo/>
其中 none
是零箭头。我们在这里处理箭头上的幺半群,对吗?
none
(≡ zeroArrow
) 应该是它的身份,所以它应该是这样的:
none <+> eelem "bar"
产生一个‘bar’元素和后续
调用应该添加另外两个元素。但是我们什么也没有!
- 如何编写
bar
箭头的正确版本以添加一个“条” 一次元素?
抱歉问了 4 个问题,但我想它们是密切相关的,所以 应该不是问题。
您似乎对 >>>
和 <+>
运算符的工作方式有些混淆。为了建立直觉,让我们首先定义两个不同的 bar
s:
bar1 :: ArrowXml a => a XmlTree XmlTree
bar1 = this <+> eelem "bar"
bar2 :: ArrowXml a => a n XmlTree
bar2 = eelem "bar"
我们首先注意到的是类型签名。 bar1
的输入类型为 XmlTree
,这意味着它以某种方式修改了现有的树,而 bar2
则丢弃了它的参数。这是由于在 bar1
中使用 this
复制了它的元素。现在,让我们将它们加载到 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/>"]
哇,它呈指数级增长!为什么?好吧,每次使用 >>>
进行组合时,您都会采用前一棵树,并且对于应用函数应用程序 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/>
前面有一个 <//>
。
好的,现在我们已经根据 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
类型的值,returns 需要一个 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>"]