如果我正确理解您的 XML,那么您的所有图表本质上都是步骤序列,其中没有任何步骤可以省略,并且每个步骤都可能有多种选择。(因此,通过该图的一组路径本质上是各种备选方案的笛卡尔积。)如果这不是真的,那么接下来的就不是你想要的了。
在这里获得笛卡尔积的最简单方法是使用 XQuery FLWOR 表达式,其中for
笛卡尔积中的每个因子都有一个子句,如 Jens Erat 的初始答案所示。
如果您事先不知道会有多少因子(因为您不知道图中可能出现的“类型”值序列),并且不想每次都重新制定查询,那么最简单的做法是编写一个递归函数,它将一系列“类型”值作为一个参数,将您正在处理的“根”元素作为另一个参数,并一次处理一个因素。
对于您的示例输入,此功能完成了这项工作:
declare function local:cartesian-product(
$doc as element(),
$types as xs:string*
) as xs:string* {
(: If we have no $types left, we are done.
Return the empty string. :)
if (empty($types)) then
''
(: Otherwise, take the first value off the
sequence of types and return the Cartesian
product of all Words with that type and
the Cartesian product of all the remaining
types. :)
else
let $t := $types[1],
$rest := $types[position() > 1]
for $val in $doc/Word[@Type = $t]/@Value
for $suffix in
local:cartesian-product($doc,$rest)
return concat($val, $suffix)
};
唯一剩下的问题是按文档顺序获取不同“类型”值的序列有点棘手。我们可以调用distinct-values($doc//Word/@Type)
来获取值,但不能保证它们会按文档顺序排列。
借用Dimitre Novatchev 对相关问题的解决方案,我们可以计算出适当的“类型”值序列,如下所示:
let $doc := <Root>
<Word Type="pre1" Value="A" />
<Word Type="pre1" Value="D" />
<Word Type="pre2" Value="G" />
<Word Type="pre2" Value="H" />
<Word Type="base" Value="B" />
<Word Type="post1" Value="C" />
<Word Type="post1" Value="E" />
<Word Type="post1" Value="F" />
</Root>
let $types0 := ($doc/Word/@Type),
$types := $types0[index-of($types0,.)[1]]
这将按文档顺序返回不同的值。
现在我们准备计算您想要的结果:
return local:cartesian-product($doc, $types)
结果的返回顺序与您给出的顺序略有不同;我假设您不关心结果的顺序:
AGBC AGBE AGBF AHBC AHBE AHBF DGBC DGBE DGBF DHBC DHBE DHBF