我简化了一个代表有向无环图的xml文件:
<?xml version="1.0" encoding="utf-8"?>
<items>
<item id="1">
</item>
<item id="2">
<parent idref="1" />
</item>
<item id="3">
<parent idref="1" />
</item>
<item id="4">
<parent idref="3" />
</item>
<item id="5">
<parent idref="4" />
</item>
<item id="6">
<parent idref="3" />
</item>
<item id="7">
<parent idref="4" />
</item>
<item id="8">
<parent idref="4" />
</item>
<item id="9">
<parent idref="4" />
</item>
<item id="10">
<parent idref="6" />
</item>
</items>
这种表示允许无限深度(我不知道数学术语)。每个项目都有一个且只有一个父项,但根项目除外,它没有父项。每个项目可以包含0到任意数量的子项目。每个项目都由其id标识,这是一个任意标签。
以点表示法可视化图形可能更容易。在我的文件中,更容易看到项目3有两个子节点,项目4有4:
digraph
{
1 -> {2, 3}
3 -> {4, 6}
4 -> {5, 7, 8, 9}
6 -> 10
}
计算第3项的子项数是:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:value-of select="count(items/item/parent[@idref='3'])" />
</xsl:template>
</xsl:stylesheet>
xslt样式表输出2,这是正确的。如果我将3更改为4,则输出为4,这也是正确的。
问题:我想计算特定项目的所有子项。
对于第3项,正确答案是7(项目4,6,5,7,8,9,10)。对于第1项,答案是9.对于第7项,它是0.我怀疑答案涉及递归,我已成功用于构建树的部分,但不传递值或计算总和。
答案 0 :(得分:3)
对于以下交叉引用,我建议设置密钥然后,为了解决问题,递归是解决方案的另一个关键,所以在XSLT 2或3中你可以用递归函数做到:
(********************************************)
(* Extra example: a parameterized signature *)
(* *)
(* It can be noted that this feature of Coq *)
(* module types has no equivalent in OCaml… *)
(********************************************)
Module Type MT2 (M : MT).
Parameter u : M.t.
Import M. (* => we can now write t rather than M.t *)
Parameter f : t -> t.
End MT2.
(* Another parameterized module *)
Module F2 (M : MT) (M2 : MT2 M).
Import M2.
Definition fu := f u.
End F2.
(* Modules implementing MT and MT2 *)
Module M <: MT.
Definition t := bool.
End M.
Module M2 <: MT2 M.
Definition u := false.
Definition f := negb.
End M2.
(* Instantiation *)
Module FM2 := F2 M M2.
Compute FM2.fu.
https://xsltfiddle.liberty-development.net/bdxtpH/0和https://xsltfiddle.liberty-development.net/bdxtpH/1以及https://xsltfiddle.liberty-development.net/bdxtpH/2就是一些例子。
对于XSLT 2,您可以使用本地<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf"
version="3.0">
<xsl:param name="start-id">7</xsl:param>
<xsl:key name="id" match="item" use="@id"/>
<xsl:key name="child" match="item" use="parent/@idref"/>
<xsl:function name="mf:descendants" as="element(item)*">
<xsl:param name="item" as="element(item)*"/>
<xsl:sequence
select="let $children := key('child', $item/@id, root($item))
return ($children | $children!mf:descendants(.))"/>
</xsl:function>
<xsl:template match="/">
<xsl:value-of select="count(mf:descendants(key('id', $start-id)))"/>
</xsl:template>
</xsl:stylesheet>
代替上面使用的xsl:variable
:
let $children
http://xsltransform.hikmatu.com/pPgCcou
对于XSLT 1,您可以使用稍微不同的方法,使用递归的命名模板收集后代,直到找不到更多子项,然后输出计数:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf">
<xsl:param name="start-id">1</xsl:param>
<xsl:key name="id" match="item" use="@id"/>
<xsl:key name="child" match="item" use="parent/@idref"/>
<xsl:function name="mf:descendants" as="element(item)*">
<xsl:param name="item" as="element(item)*"/>
<xsl:variable name="children" select="key('child', $item/@id, root($item))"/>
<xsl:sequence
select="$children | $children/mf:descendants(.)"/>
</xsl:function>
<xsl:template match="/">
<xsl:value-of select="count(mf:descendants(key('id', $start-id)))"/>
</xsl:template>
</xsl:transform>
https://xsltfiddle.liberty-development.net/3NzcBsK/0和 https://xsltfiddle.liberty-development.net/3NzcBsK/1和 https://xsltfiddle.liberty-development.net/3NzcBsK/2有样本数据。
答案 1 :(得分:0)
只是为了变化,这是另一种方法:
<xsl:param name="start-id">7</xsl:param>
<xsl:key name="id" match="item" use="@id"/>
<xsl:function name="f:has-ancestor" as="xs:boolean">
<xsl:param name="d" as="item"/>
<xsl:param name="a" as="item"/>
<xsl:sequence select="exists($d) and ($a is $d or
f:has-ancestor(key('id', $d/parent/@idref), $a))"/>
</xsl:function>
然后
select="count(//item[f:has-ancestor(., key('id', $start-id))])"/>
(当然,哪一种更快取决于你树木的浓密感。)