+ =运算符的固执

马克·卡波夫

正如我们从上一个问题中学到的那样+=运算符允许一次添加一个元素。是否可以“检测”以前添加的元素并控制将来如何添加?

这是一个开始调查的简单程序:

module Main (main) where

import Control.Monad (void)
import Text.XML.HXT.Core

main :: IO ()
main = void $ runX $ root [] [foo]
       >>> writeDocument [withIndent yes] "test.xml"

foo :: ArrowXml a => a XmlTree XmlTree
foo = eelem "foo" += bar += bar += bar -- += is left associative

bar :: ArrowXml a => a XmlTree XmlTree
bar = ifA (deep (hasName "bar")) (eelem "baz") (eelem "bar")

在这里,foo创建“ foo”节点及其内容。内容是用bar箭头生成的,它应该足够聪明以检测先前添加的“ bar”元素并更改其行为。这里我们使用deep简单:如果'bar'元素是'foo'元素的子元素,则无论它有多深,都应将其检测出来(也getChildren >>> hasName "bar"应做一些技巧)。

因此,test.xml文件的预期内容为

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

当然,它不起作用。这是我得到的:

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

我的问题:

  1. 为什么bar箭头无法检测到“ bar”元素

  2. 如何检测?

CR Drost

这是类型签名真正有用的情况之一。凝视一下类型签名:

(+=) :: (ArrowXml a) => a b XmlTree -> a b XmlTree -> a b XmlTree 

首先ArrowXml是的子类Arrow,它描述了某种将一些输入输入到某些输出的机器。您可以把它想象成一个带有传送带的大工厂,将传送带运送到不同的机器上,我们正在建造这些工厂机器,从而建造具有功能的工厂。例如,箭头组合器中的三个是:

(&&&) :: (Arrow a) => a b c -> a b c' -> a b (c, c') |infixr 3|
  Fanout: send the input to both argument arrows and combine their output.

arr :: (Arrow a) => (b -> c) -> a b c
  Lift a function to an arrow.

(.) :: (Category cat) => cat b c -> cat a b -> cat a c
  morphism composition.

现在,请仔细查看小写字母(类型变量):

(+=) :: (ArrowXml a) => a b XmlTree -> a b XmlTree -> a b XmlTree 

显然,我们要使用两台将bs转换为XmlTrees的机器,并将它们“合并到一起”到一台使用bin并推出的机器XmlTree但重要的是,此类型签名告诉我们或多或少可以实现此目的唯一方法是:

arr1 += arr2 = arr f . (arr1 &&& arr2) where
    f :: (XmlTree, XmlTree) -> XmlTree
    f = _

这是因为“自由定理”;如果你不知道参数的类型,那么我们就可以证明你真的不能太多吧。(这可能会稍微复杂一点,因为箭头可能具有未被完全封装的结构arr,例如内部计数器,这些计数器在使用时会加在一起,.然后设置为。因此,实际替换为泛型后,您可以去。)0arrarr fa (XmlTree, XmlTree) XmlTree

因此,我们必须在此处并行执行两个箭头。这就是我要说的。因为组合(+=)器不知道是什么b,所以别无选择,只能将bb并行地顺畅地馈入箭头,然后尝试将其输出组合在一起。所以,deep (hasName "bar")不看foo

deep (hasName "bar") . foo如果您确实愿意,可以与之建立一个相互递归的解决方案,但是这似乎有潜在的危险(即无限循环),因此简单地定义如下内容可能会更安全:

a ++= b = a += (b . a)

其中“当前” a被“馈送”到b以产生更新。为此,您必须从中导入.Control.Category因为它与导入不同Prelude..(仅用于合成)。看起来像:

import Prelude hiding ((.))
import Control.Category ((.))

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章