Xquery: same test has different result whether used in switch/case or in if/then/else

泪湿孤枕 提交于 2019-12-10 13:35:13

问题


I can't find an explanation on the following. I've made this test script in order to solve my previous question.

xquery version "3.0" ;
declare default function namespace 'local' ;

declare function local:is-img-only( $element as element() )  as xs:boolean {    
   ($element/child::*[1] instance of element(img)) 
   and (fn:not($element/child::*[2])) 
   and (fn:normalize-space($element) = '')
} ;

let $in-xml := <myxml>
   <p id="1">

      <img id="1"/>

   </p>
   <p id="2">
      <img id="1"/>
      hello
   </p>
   <p id="3">
      <img id="1"/>
   </p>
   <p id="4">
    <blockquote>hello</blockquote>
      <img id="1"/>
   </p>
   <p id="5">
      <img id="1"/>
      <img id="2"/>
   </p>
</myxml>

Then, the following using if then else :

for $p in $in-xml/p
  return if (local:is-img-only($p)) 
    then $p/@id/fn:data() || ' has only an img child'
    else $p/@id/fn:data() || ' has not strictly an img child'

returns as expected :

1 has only an img child
2 has not strictly an img child
3 has only an img child
4 has not strictly an img child
5 has not strictly an img child  

Whereas the following using switch case

for $p in $in-xml/p
  return switch ($p)
    case (local:is-img-only($p)) return $p/@id/fn:data() || ' has only an img child'
    default return $p/@id/fn:data() || ' has not strictly an img child'

returns as not expected :

1 has not strictly an img child
2 has not strictly an img child
3 has not strictly an img child
4 has not strictly an img child
5 has not strictly an img child

Any explanation ? Why Conditional Expressions would not behave the same way than Switch Expressions ?


回答1:


Great job in locating the relevant sections of the XQuery specification for your question! That's half the battle. Switch expressions do share some things in common with conditional expressions, but there are some differences.

The key difference is that a conditional expression evaluates a test expression to see if the result is true or false, while a switch expression compares one expression against one or more other expressions to find the first pair that are equal. Using pseudo code, we can illustrate the difference. All of the logic in a conditional expression takes place here on one line:

if ($EXPRESSION)

In contrast, the logic of a switch expression is spread over many lines:

switch ($EXPRESSION_A)
    case ($EXPRESSION_B) ...
    case ($EXPRESSION_C) ...
    case ($EXPRESSION_D) ...
    default ...

The switch expression is actually performing a chain of comparisons, which we could express as even more lines of conditional expressions:

if (deep-equal($EXPRESSION_A, $EXPRESSION_B))
then ...
else 
    if (deep-equal($EXPRESSION_A, $EXPRESSION_C))
    then ...
    else ...
        if (deep-equal($EXPRESSION_A, $EXPRESSION_D))
        then ...
        else ... (: "default" :)

The nitty gritty differences between these two expressions are described in the spec, starting with where they describe "the first step" in processing each expression. Whereas the first step in processing a conditional expression is:

to find the effective boolean value of the test expression.

... the first step in processing a switch expression is:

to apply atomization to the value of the switch operand expression.

Let's return to your concrete example and take a look at your conditional's test expression and your switch's switch operand expression:

  1. Your conditional's test expression:

    if (local:is-img-only($p)) 
    
  2. Your switch's operand expression:

    switch ($p)
    

The conditional's test expression returns a boolean - true() or false(), so this condition clearly charts out the path of the rest of the code.

In contrast, the logic of the switch expression has only just begun with this operand expression. First, it finds the atomized value of the operand expression—the <p> element bound to the $p variable in your FLWOR expression. Since this value depends on which <p> we're looking at, the atomized value will either be an empty string (""), whitespace, or "hello" (or some combination thereof, depending on whitespace in your source and your boundary space declaration). Then, the switch's first case operand is evaluated and atomized. Your first case operand is as follows:

case (local:is-img-only($p))

This expression, as we'll recall, evaluates to a boolean. The next step the switch expression performs is to compare the atomized value of the switch operand expression with the atomized value of the switch case operand using the fn:deep-equal function. Effectively, then, we're asking the XQuery processor to perform the following comparisons:

deep-equal("", true())
deep-equal("hello", false())

In both cases the comparison returns false(). Thus, this comparison in our case operand always fails, so the switch expression is falling back on the default clause in every iteration of your FLWOR expression.

A switch expression that mimics the results of your original conditional expression would be the following:

for $p in $in-xml/p
  return switch(local:is-img-only($p))
    case (true()) return $p/@id/fn:data() || ' has only an img child'
    default return $p/@id/fn:data() || ' has not strictly an img child'

This performs the following checks:

deep-equal(true(), true())
deep-equal(true(), false())

And returns the identical results as your conditional expression.

This isn't a particularly compelling use for the switch expression - since we're effectively evaluating a single test expression. The switch expression really shines when you have many values to compare. The spec offers us a good example switch expression to consider:

switch ($animal)
    case "Cow" return "Moo"
    case "Cat" return "Meow"
    case "Duck" return "Quack"
    default return "What's that odd noise?"

This is far more readable and compact than the equivalent conditional expression:

if (deep-equal($animal, "Cow")) 
then "Moo"
else 
    if (deep-equal($animal, "Cat")) 
    then "Meow"
    else
        if (deep-equal($animal, "Duck"))
        then "Quack"
        else "What's that odd noise?"

Or even the more straight-forward interpretation:

if ($animal eq "Cow")
then "Moo"
else 
    if ($animal eq "Cat")
    then "Meow"
    else
        if ($animal eq "Duck")
        then "Quack"
        else "What's that odd noise?"

The upshot: if you ever find yourself writing a chain of conditionals, and the left side of the comparison is always the same, consider making a switch.



来源:https://stackoverflow.com/questions/48199042/xquery-same-test-has-different-result-whether-used-in-switch-case-or-in-if-then

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!