一次添加一个元素的箭头 [英] Arrow to add one element at a time

查看:104
本文介绍了一次添加一个元素的箭头的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题与HXT有关,但我想它通常适用于
ArrowPlus 的概念。请考虑以下程序:

This question is about HXT, but I guess it's applicable to conception of ArrowPlus in general. Consider the following program:

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中保存什么吗? ?我的期望:

Can you tell what will be saved in test.xml? My expectations:

<?xml version="1.0" encoding="UTF-8"?>
<foo>
  <bar/>
  <bar/>
  <bar/>
</foo>

我的逻辑:箭头 bar 复制所有输入并添加一个 bar元素
是标识箭头的别名):

My logic: arrow bar copies all its input and adds one ‘bar’ element (this is alias for identity arrow):

 |     |
this  eelem "bar"
 |     |
 \     /
  \   /
   <+>
    |

因此, bar>>>的结果栏>>> bar 应该是三个 bar元素(注意
eelem bar>>> eelem bar 结果只产生一个'bar'元素
,因为 mkelem 族的箭头会忽略其输入(尽管仍然可以使用
来生成其内容)并仅输出新创建的元素。)

So, result of bar >>> bar >>> bar should be three ‘bar’ elements (note that eelem "bar" >>> eelem "bar" will result in only one ‘bar’ element, since arrows of mkelem family ignore their input (although it still can be used to generate their contents) and output only newly created element).

话虽如此,我在执行后显示 test.xml 的内容
的程序:

Having said all that, I present contents of test.xml after execution of the program:

<?xml version="1.0" encoding="UTF-8"?>
<foo>
  <//>
  <bar/>
  <bar/>
  <bar/>
  <bar/>
  <bar/>
  <bar/>
  <bar/>
</foo>

问题:


  1. 什么是< //>

为什么有7个'bar'元素而不是3?
重复的原因是什么?

Why there are 7 ‘bar’ elements instead of 3? What is reason of this duplication?

为什么当我替换 bar>>>时,栏>>> bar none>>>栏>>>栏>>>
bar
我得到:

Why when I replace bar >>> bar >>> bar with none >>> bar >>> bar >>> bar I get:



   <?xml version="1.0" encoding="UTF-8"?>
   <foo/>

其中 none 是零箭头。我们在这里处理箭头上的monoid,对吗?
none (≡ zeroArrow )应该是它的身份,所以它应该像:
无< +> eelem bar 会生成一个 bar元素,随后的
调用应添加另外两个元素。

where none is zero arrow. We deal with monoid on arrows here, right? none (≡ zeroArrow) should be identity for it, so it should go like: none <+> eelem "bar" which produces a ‘bar’ element and subsequent calls should add two another elements. But we don't get any!


  1. 如何编写 bar的正确版本 code>箭头一次可以添加一个 bar
    元素?

  1. How to write proper version of bar arrow that would add one ‘bar’ element at a time?

很抱歉提出了四个问题,但我想它们是密切相关的,因此
应该不是问题。

Sorry for asking 4 questions, but I guess they are closely related, so it shouldn't be a problem.

推荐答案

您在>> ++ c运算符的工作方式之间有些困惑。为了建立直觉,我们首先定义两个不同的 bar s:

It seems like you have some confusion between how the >>> and <+> operators work. To build intuition, let's first define two different bars:

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 中,弄清楚>> < +> 一起工作:

The first thing we notice is the type signature. bar1 has an input type of XmlTree, meaning that it modifies an existing tree in some way, whereas bar2 discards its argument. This is due to the use of this in bar1 that copies its elements. Now, let's load these up in ghci to figure out how >>> and <+> work together:

Prelude Text.XML.HXT.Core> runX $ xshow $ bar2
["<bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ bar2 >>> bar2 >>> bar2
["<bar/>"]

嗯,这很奇怪不管我们用>>> 进行多少次构建,都会保持相同的结构。发生这种情况是因为对于 bar2 ,我们每次对其进行转换时都会丢弃该树:记住它的类型签名是一个XmlTree 而不是 XmlTree XmlTree 。让我们将其与 bar1

Hm, that's weird, it keeps creating the same structure no matter how many times we compose it with >>>. This happens because for bar2, we're discarding the tree each time we transform it: remember it's type signature is a n XmlTree instead of a XmlTree XmlTree. Let's compare that to 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/>"]

哇,它呈指数增长!为什么?好吧,每次您用>>> 编写时,就采用了前一棵树,并且对于每个应用功能应用程序的元素< +> eelem bar bar1 的第一次调用没有先前的树,因此成为根节点,您只需附加< bar /> 。但是,对于 bar1>> bar1 ,第一个 bar1 将创建< //>< bar /> ,第二个将组成< //>< bar /> 的每个节点与 bar1 再次导致:

Woah, it's growing exponentially! Why? Well, each time you compose with >>>, you're taking the previous tree, and for each element applying the function application this <+> eelem "bar". The first invocation of bar1 has no previous tree, so this becomes the root node and you simply append an element of <bar/> to it. However, for bar1 >>> bar1, the first bar1 will create <//><bar/> and the second one will compose each node of <//><bar/> with bar1 again, leading to:

bar1 === <//><bar/>
bar1 >>> bar1 === <//><bar/><bar/><bar/>
                  |--------||----------|
                    First      Second

现在您继续这样做,您可以看到 bar1>>> bar1>>> bar1 会产生七个< bar /> ,然后是< //>

Now you keep doing that, and you can see how bar1 >>> bar1 >>> bar1 will produce seven <bar/>s preceded by a <//>.

好,现在我们有了>> 的直觉 ArrowXml 我们可以看到< +> 的行为:

OK, so now that we have intuition for >>> in terms of ArrowXml we can see how <+> behaves:

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 仍然可以转换类型为的XmlTree XmlTree 的值,因此,如果将其与>>> 。看看是否可以将输出的内容整理一下:

Oh, that's pretty simple... they just append one after the other. We can see that the type of bar1 <+> bar1 is still one that transforms values of type a XmlTree XmlTree, so you will get some crazy behavior if you combine it with >>>. See if you can wrap your mind around this output:

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 ,检查其类型签名:

To answer your question about none, check it's type signature:

Prelude Text.XML.HXT.Core> :t none
none :: ArrowList a => a b c

对我来说,它的值类型为 b 并返回 c 类型的值。由于我们不知道 c 是什么(我们没有将其作为 none 的参数提供),因此我们可以假设它将是空集。这与我们之前的>>> < +> 的定义是合理的:

To me that says it takes a value of type b and returns a value of type c. Since we have no idea what c is (we didn't provide it as an argument to none), we can assume that it's going to be the empty set. This makes sense with our previous definitions of >>> and <+>:

Prelude Text.XML.HXT.Core> runX $ xshow $ none <+> bar1
["<//><bar/>"]
Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> bar1
[""]

在第一种情况下,空文档附加了另一个文档基本上是身份操作。在第二个中, none 不产生任何元素,因此当您使用 bar1 组成它时,没有要操作的元素,因此结果是空文档。实际上,由于Haskell懒惰,我们可以更轻松地了解 none 的内容,因为我们知道它永远不会被评估:

In the first case, the empty document appended with another document is basically the identity operation. In the second one, none produces no elements, so when you compose it with bar1, there are no elements to operate on, and hence the result is the empty document. In fact, since Haskell is lazy we can be even more cavalier about what we compose with none since we know that it'll never be evaluated:

Prelude Text.XML.HXT.Core> runX $ xshow $ none >>> undefined
[""]

鉴于此知识,您可能想做的是像这样:

Given this knowledge, what you were probably trying to do was something like this:

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>"]

这篇关于一次添加一个元素的箭头的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆