使用示例

时间:2017-03-22 10:37:57

标签: xquery

我有一个看起来像这样的数据库(通过$database访问它):

<country car_code="F" area="547030" capital="cty-france-paris">
  <name>France</name>
  <border country="AND" length="60"/>
  <border country="E" length="623"/>
  <border country="D" length="451"/>
  <border country="I" length="488"/>
  <border country="CH" length="573"/>
  <border country="B" length="620"/>
  <border country="L" length="73"/>
  <border country="MC" length="4.4"/>
</country>

.....
other countries

我想写一个功能,通过陆地边界提供从法国(或任何其他国家)可以到达的所有国家的名称。第一次尝试(可能有很多语法错误和其他错误,但程序的语义应该“更清楚”):

declare function local:reachable($country as element())
  as (return value should be a sequence of countries  )
{
  if $country == ()   (:if empty, it doesn't border to any other country:)
    then ()

  else(
    $country/name  UNION (for $bord in $country/border/@country  return
    local:reachable ($database/country/car_code = @bord ))
  )
}

对该功能的调用:

local:reachable($database/country[@car_code = "F"])

与法国接壤的国家应该是:

  <border country="AND" length="60"/>
  <border country="E" length="623"/>
  <border country="D" length="451"/>
  <border country="I" length="488"/>
  <border country="CH" length="573"/>
  <border country="B" length="620"/>
  <border country="L" length="73"/>
  <border country="MC" length="4.4"/>

但我们还需要找到这些国家的边境国家。 最终输出应为“F”,“AND”,“E”,“D”,“I”,“CH”,“B”,“L”,“MC”......,X,Y,Z, (以及与这些国家接壤的其他国家)。

  • 我知道UNION没有定义,但还有什么我可以使用吗?我只是想让它更清楚我想做什么

  • 除了语法错误之外,一个大问题是,如果“F”与“L”接壤,那么“L”将与“F”接壤,因此我的“功能”永远不会终止 - 我该如何处理是什么?

  • 我可以从语法

  • 获得一些帮助
  • 如果问题不明确请告诉我,以便我可以进一步澄清

1 个答案:

答案 0 :(得分:5)

开始之前

以下是对您的代码的一些评论:

  • $country as element()定义了必须包含的变量 恰好是一个元素,所以它永远不会是空的;使用element()? if 如果可以有任意数量的元素,则该元素是可选的element()* 他们,或element()+,如果必须有一个或多个

  • 序列运算符,可用于构造序列 其他序列:(1,2) , (3,4)构造2个序列:(1,2)(3,4),然后构造包含所有项目的另一个 其他人,导致:(1,2,3,4)

数据

让我略微改变countries元素,所以我删除了噪音, 并使这个演示更简单一点。另外,我创建了一个 简单但完整的地图。我们说我们有两个相邻的国家 和K,以及另外4个形成一个正方形(每个国家的邻居都是2 其他):N,G,B和F.与现有地理或任何地理相似 政治只在你眼中: - )

<countries>
   <country id="U">
      <name>Over the top</name>
      <border idref="K"/>
   </country>
   <country id="K">
      <name>Beyond the see</name>
      <border idref="U"/>
   </country>
   <country id="N">
      <name>Flatland</name>
      <border idref="B"/>
      <border idref="G"/>
   </country>
   <country id="G">
      <name>Marxhome</name>
      <border idref="N"/>
      <border idref="F"/>
   </country>
   <country id="B">
      <name>Beerium</name>
      <border idref="N"/>
      <border idref="F"/>
   </country>
   <country id="F">
      <name>Grapeandcheese</name>
      <border idref="B"/>
      <border idref="G"/>
   </country>
</countries>

解决方案

该解决方案包含一个消耗队列的递归函数 国家要处理。同时,它累积结果列表一 国家/地区。它将队列中的第一个国家/地区添加到其中 结果,然后递归所有相邻的国家 已经在队列中,也没有当前的结果。增强的结果是 也传了下来。

xquery version "3.0";

(: Map:
 : U K | N G
 :       B F
 :)
declare variable $countries :=
<countries>
   <country id="U">
      <name>Over the top</name>
      <border idref="K"/>
   </country>
   <country id="K">
      <name>Beyond the see</name>
      <border idref="U"/>
   </country>
   <country id="N">
      <name>Flatland</name>
      <border idref="B"/>
      <border idref="G"/>
   </country>
   <country id="G">
      <name>Marxhome</name>
      <border idref="N"/>
      <border idref="F"/>
   </country>
   <country id="B">
      <name>Beerium</name>
      <border idref="N"/>
      <border idref="F"/>
   </country>
   <country id="F">
      <name>Grapeandcheese</name>
      <border idref="B"/>
      <border idref="G"/>
   </country>
</countries>;

declare function local:reachable(
   $queue  as element(country)*,
   $result as element(country)*   
) as element(country)*
{
   if ( empty($queue) ) then (
      (: we do not consider one country reachable from itself :)
      tail($result)
   )
   else (
      let $this := head($queue)
      let $rest := tail($queue)
      let $more := $this/border/@idref[not(. = ($queue, $result)/@id)]
      return
         local:reachable(
            ( $rest, $countries/country[@id = $more] ),
            ( $result, $this ))
   )
};

(: for each countries, display its reachable countries
 :)
for $c in $countries/country
order by $c/@id
let $r := local:reachable($c, ())
return
   $c/name || ': ' || string-join($r/@id, ', ')

结果

Beerium: N, G, F
Grapeandcheese: N, G, B
Marxhome: N, B, F
Beyond the see: U
Flatland: G, B, F
Over the top: K