简体   繁体   English

如何在XQuery中处理递归?

[英]How to handle recursion in XQuery?

I'm trying to find all countries which are reachable by land by traversing from one country to another via land borders using the mondial.sql database. 我试图通过使用mondial.sql数据库通过陆地边界从一个国家到另一个国家来寻找可以通过陆地到达的所有国家。 It must be done recursively and I found some functions online which I thought would be useful for joining sequences and to be able to exclude countries which have already been found. 它必须以递归方式完成,我发现一些在线功能,我认为这对于连接序列很有用,并且能够排除已经找到的国家。

The problem is I end up in a loop even though the countries that are to be excluded seems to be handled properly. 问题是,即使被排除的国家似乎得到妥善处理,我最终也会陷入困境。 So my thinking is that I might have to define a base case in some way to make the recursion stop once all possible countries have been found. 所以我的想法是,我可能必须以某种方式定义一个基本案例,以便在找到所有可能的国家后使递归停止。 How to achieve this with XQuery? 如何使用XQuery实现这一目标?

(:functx.value-union and is-value-in-sequence were found at http://www.xqueryfunctions.com/xq/:)
declare namespace functx = "http://www.functx.com";
declare function functx:value-union
  ( $arg1 as xs:anyAtomicType* ,
    $arg2 as xs:anyAtomicType* )  as xs:anyAtomicType* {

  distinct-values(($arg1, $arg2))
 };

 declare function functx:is-value-in-sequence
  ( $value as xs:anyAtomicType? ,
    $seq as xs:anyAtomicType* )  as xs:boolean {

   $value = $seq
 } ;

(:Recursive function for finding reachable countries:)
declare function local:findReachable($countries, $country, $reachedCountries) {
    let $reachableCountries := $countries[@car_code = $country/border/@country]
    for $c in $reachableCountries
    where not(functx:is-value-in-sequence($c, $reachedCountries))
    return functx:value-union($c, local:findReachable($countries, $c, functx:value-union($reachableCountries, 
    $reachedCountries)))
};

let $countries := //country
let $startingCountry := //country[@car_code = 'S']
return local:findReachable($countries, $startingCountry, $startingCountry)

Your checks with $reachedCountries only guarantee that countries do not appear twice on the same path , but you still visit every country along every possible path, which takes a long time. 您使用$reachedCountries进行的检查仅保证国家/地区不会在同一路径上出现两次,但您仍然会沿着每条可能的路径访问每个国家/地区,这需要很长时间。 There is no loop, just lots of redundancy. 没有循环,只有很多冗余。

Here is a simple depth-first search that does what you want: 这是一个简单的深度优先搜索 ,可以满足您的需求:

declare function local:dfs($stack, $seen) {
  if(empty($stack)) then $seen
  else (
    let $country := $stack[1]
    let $neighbors :=
        for $code in $country/border/@country[not(. = $seen/@car_code)]
        return $country/../country[@car_code = $code]
    return local:dfs(($neighbors, $stack[position() > 1]), ($seen, $neighbors))
  )
};

local:dfs(doc('mondial.xml')//country[@car_code = 'S'], ())/name

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM