代码之家  ›  专栏  ›  技术社区  ›  laconis

Xquery:无论是在switch/case还是if/then/else中使用,相同的测试都有不同的结果

  •  6
  • laconis  · 技术社区  · 8 年前

    我找不到关于以下内容的解释。我制作这个测试脚本是为了解决 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>
    

    然后,以下使用 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'
    

    按预期返回:

    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  
    

    鉴于以下使用 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'
    

    意外返回:

    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
    

    有什么解释吗?为什么? Conditional Expressions 不会表现出与 Switch Expressions ?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Joe Wicentowski    8 年前

    为您的问题找到XQuery规范的相关部分做得很好!这是成功的一半。Switch表达式确实与条件表达式有一些共同之处,但也有一些区别。

    关键区别在于条件表达式 计算测试表达式 查看结果是真是假,而开关表达式 将一个表达式与一个或多个其他表达式进行比较 找到第一对相等的。通过使用伪代码,我们可以说明这种差异。条件表达式中的所有逻辑在此发生在一行:

    if ($EXPRESSION)
    

    相反,switch表达式的逻辑分布在许多行上:

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

    switch表达式实际上正在执行一系列比较,我们可以将其表示为更多行条件表达式:

    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" :)
    

    规范中描述了这两个表达式之间的本质区别,从它们描述处理每个表达式的“第一步”开始。而处理条件表达式的第一步是:

    查找测试表达式的有效布尔值。

    ... 处理开关表达式的第一步是:

    对开关操作数表达式的值应用原子化。

    让我们回到具体示例,看看条件的测试表达式和开关的开关操作数表达式:

    1. 条件的测试表达式:

      if (local:is-img-only($p)) 
      
    2. 开关的操作数表达式:

      switch ($p)
      

    条件的测试表达式返回布尔值- true() false() ,因此此条件清楚地列出了其余代码的路径。

    相反,switch表达式的逻辑才刚刚从这个操作数表达式开始。首先,它查找操作数表达式的原子化值 <p> 元素绑定到 $p FLWOR表达式中的变量。因为该值取决于 <p> 我们看到,原子化值要么是空字符串( "" ),空白,或 "hello" (或其组合,具体取决于源代码中的空白和边界空间声明)。然后,对开关的第一个case操作数求值并进行原子化。第一个大小写操作数如下:

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

    我们还记得,这个表达式的计算结果是布尔值。开关表达式执行的下一步是使用 fn:deep-equal 作用实际上,我们要求XQuery处理器执行以下比较:

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

    在这两种情况下,比较都会返回 false() . 因此,本例中的比较是操作数 总是 失败,因此开关表达式返回到 default 在FLWOR表达式的每次迭代中。

    模拟原始条件表达式结果的开关表达式如下所示:

    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'
    

    这将执行以下检查:

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

    并返回与条件表达式相同的结果。

    这并不是switch表达式的一个特别引人注目的用途,因为我们正在有效地评估单个测试表达式。当您有许多值要比较时,switch表达式真的很有用。规范为我们提供了一个很好的开关表达式示例:

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

    这比等效的条件表达式更具可读性和紧凑性:

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

    或者更直接的解释:

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

    结果是:如果你发现自己在写一系列条件句,而比较的左边总是一样的,那么考虑一下 进行切换 .