2017-05-07 71 views
4

我试图通过使用mondial.sql数据库通过陆地边界从一个国家穿越到另一个国家,找到所有可通过陆地到达的国家。它必须以递归方式完成,并且我发现了一些在线的函数,我认为这些函数对于加入序列是有用的,并且能够排除已经找到的国家。如何处理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) 

回答

6

您与$reachedCountries检查只能保证国家不出现两次相同的路径上,但你仍然参观所有国家一起每一个可能的路径,这需要很长的时间。没有循环,只是很多的冗余。

下面是一个简单深度优先搜索,你想要做什么:

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