2017-05-07 68 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