【问题标题】:How to use xquery to perform four arithmetic operations on xml nodes如何使用xquery对xml节点进行四次算术运算
【发布时间】:2021-10-25 03:14:43
【问题描述】:
<Expression>
            <description/>
            <minus>
                <plus>
                    <int value="2"/>
                    <int value="3"/>
                    <times>
                        <int value="5"/>
                        <minus>
                            <int value="6"/>
                            <int value="3"/>
                        </minus>
                    </times>
                </plus>
                <int value="4"/>
            </minus>
        </Expression>

我想得到这样的表达式并输出结果:(2+3+(6-3)*5)-4=16

【问题讨论】:

    标签: xquery


    【解决方案1】:

    这是高阶函数的一个很好的用例:

    declare variable $evaluators := map {
       'int' : function($e){xs:integer($e/@value)},
       'plus' : function($e){sum($e/*/local:eval(.)},
       'minus' : function($e){local:eval($e/*[1] - local:eval($e/*[2])},
       'times' : function($e){local:eval($e/*[1] * local:eval($e/*[2])},
       ...
    }
    declare function local:eval($e as element(*)) {
       $evaluators(name($e))($e)
    }
    local:eval(Expression/(* except description))
    

    细节取决于你没有与我们分享的规范方面,例如description是否可以出现在任何地方,times(如plus)是否可以接受两个以上的操作数等.

    致谢:我在 1972 年看到我已故的朋友和同事 Andrew Birrell (https://cacm.acm.org/news/210689-in-memoriam-andrew-birrell-1951-2016/fulltext) 使用 Algol 68 解决了这个问题,正是这个问题和解决方案让我相信将函数视为一流对象的价值。

    【讨论】:

    • 注意,Martin Honnen 的解决方案输出表达式(2+3+(6-3)*5)-4 以及结果16。我假设你只是想要结果。
    【解决方案2】:

    这是一个可由 Saxonica 的 SaxonCS 运行的 XSLT 4 示例:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="4.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all"
      expand-text="yes">
      
      <xsl:output method="text"/>
    
      <xsl:mode on-no-match="shallow-skip"/>
      
      <xsl:strip-space elements="*"/>
      
      <xsl:param name="operator-map" as="map(xs:string, xs:string)"
        select="map { 'minus' : ' - ',
                      'plus' : ' + ',
                      'times' : ' * ' }"/>
    
      <xsl:template match="Expression">
        <xsl:variable name="expression">
          <xsl:apply-templates/>
        </xsl:variable>
        <xsl:variable name="result" as="xs:decimal">
          <xsl:evaluate xpath="$expression"/>
        </xsl:variable>
        <xsl:value-of select="$expression, $result" separator=" = "/>
      </xsl:template>
      
      <xsl:template match="minus | plus | times">
        <xsl:text>(</xsl:text>
        <xsl:apply-templates separator="{$operator-map(local-name())}"/>
        <xsl:text>)</xsl:text>
      </xsl:template>
      
      <xsl:template match="int[@value]">
        <xsl:value-of select="@value"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    或作为 XSLT 3:

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="3.0"
      xmlns:xs="http://www.w3.org/2001/XMLSchema"
      exclude-result-prefixes="#all"
      expand-text="yes">
      
      <xsl:output method="text"/>
    
      <xsl:mode on-no-match="shallow-skip"/>
      
      <xsl:strip-space elements="*"/>
      
      <xsl:param name="operator-map" as="map(xs:string, xs:string)"
        select="map { 'minus' : ' - ',
                      'plus' : ' + ',
                      'times' : ' * ' }"/>
    
      <xsl:template match="Expression">
        <xsl:variable name="expression">
          <xsl:apply-templates/>
        </xsl:variable>
        <xsl:variable name="result" as="xs:decimal">
          <xsl:evaluate xpath="$expression"/>
        </xsl:variable>
        <xsl:value-of select="$expression, $result" separator=" = "/>
      </xsl:template>
      
      <xsl:template match="minus | plus | times">
        <xsl:if test="position() gt 1"> {$operator-map(local-name(..))} </xsl:if>
        <xsl:text>(</xsl:text>
        <xsl:apply-templates/>
        <xsl:text>)</xsl:text>
      </xsl:template>
      
      <xsl:template match="int[@value]">
        <xsl:if test="position() gt 1"> {$operator-map(local-name(..))} </xsl:if>
        <xsl:value-of select="@value"/>
      </xsl:template>
    
    </xsl:stylesheet>
    

    XQuery (3) 中的单独评估可能为

    declare function local:evaluate($expression)
    {
      local:evaluate-operator($expression/(* except description))
    };
    
    declare function local:evaluate-operator($operator)
    {
      typeswitch ($operator)
        case element(minus)
          return fold-left(tail($operator/*), local:evaluate-operator(head($operator/*)), function($a, $op) { $a - local:evaluate-operator($op) })
        case element(plus)
          return sum($operator/*!local:evaluate-operator(.))
        case element(times)
          return fold-left($operator/*, 1, function($a, $op) { $a * local:evaluate-operator($op) })
        case element(int)
          return xs:integer($operator/@value)
        default return ()
    };
    
    local:evaluate(Expression)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-02
      • 1970-01-01
      • 1970-01-01
      • 2013-02-06
      • 2020-10-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多