xml - 一次添加一个元素的箭头

标签 xml haskell arrows hxt monoids

这个问题是关于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>

问题:

  1. 什么是<//>

  2. 为什么有 7 个“bar”元素而不是 3 个?这是什么原因 重复?

  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放弃其论点。这是由于使用 thisbar1复制其元素。现在,让我们将它们加载到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/

    相关文章:

    java - Spring无法加载xml配置文件

    haskell - 覆盖条件失败

    html - 如何使用 -webkit-scrollbar-button 添加箭头

    php - 带有 mySQL 和 DOM 的 XML

    xml - 添加超链接到 indesign

    haskell - 在状态计算中使用 Lens.Family.LensLike' 作为 setter 和 getter

    haskell - 从 Prolog 到 Haskell 的思考——生成真值组合列表

    parquet - 为什么 `plain_dictionary` 编码的字典页面偏移量为 0?

    haskell - (\f -> fmap f id) 是否总是等价于 arr?

    java - XML 转换和换行字符