节点的Strahler number 在树中是节点高度概念的概括。
目标是定义一个转换,它将数字属性(例如strahler
)添加到XML文档的每个节点。
归纳地表示n
的节点Stn(n)
的Strahler编号定义如下:
n
是一片叶子,那么Stn(n)=0
n
不是叶子)通过降序Strahler值对其子项(n1
,...,nd
)进行排序:Stn(n1)
≥{{1} }≥
......≥
Stn(n2)
。
如果Stn(nd)
则Stn(n1) = Stn(n2)
;否则Stn(n)=n1+1
。 E.g。对于带有子Stn(n)=Stn(n1)
n
n1
的节点n2
,分别为Stn n3
1
2
,{{1}的Stn }是3
(因为最大值只出现一次)。对于带有子n
3
n
的节点n1
,分别为Stn n2
n3
1
,{{1}的Stn再次2
(因为最大值出现不止一次)。
示例输入:
2
输出:
n
答案 0 :(得分:1)
这种简短而有效的XSLT 2.0转换:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saxon="http://saxon.sf.net/"
xmlns:f="my:f">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[true()]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="strahler" select="f:strahler(.)"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:function name="f:strahler" as="xs:integer" saxon:memo-function="yes">
<xsl:param name="pElem" as="element()"/>
<xsl:sequence select=
"if(not($pElem/*))
then 1
else
if(not($pElem/*[2]))
then f:strahler($pElem/*[1])
else for $maxVal in max($pElem/*/f:strahler(.)),
$twoWithMax in
(($pElem/*[f:strahler(.) eq $maxVal])[2]/1, 0)[1]
return $maxVal+$twoWithMax
"/>
</xsl:function>
</xsl:stylesheet>
应用于提供的XML文档:
<root field="4">
<a>
<aa x="1"/>
<ab>
<aba number="36" usefulness="useful">
<abaa>text1</abaa>
<abab>
<ababa>text2</ababa>
</abab>
</aba>
<abb number="37" usefulness="useful">
<abba>text3</abba>
<abbb>
<abbba>text4</abbba>
<abbbb>text5</abbbb>
</abbb>
</abb>
</ab>
</a>
</root>
生成正确的结果(请注意, Strahler number 的官方定义会将1
赋予一片叶子节点 - 不是0):
<root field="4" strahler="3">
<a strahler="3">
<aa x="1" strahler="1"/>
<ab strahler="3">
<aba number="36" usefulness="useful" strahler="2">
<abaa strahler="1">text1</abaa>
<abab strahler="1">
<ababa strahler="1">text2</ababa>
</abab>
</aba>
<abb number="37" usefulness="useful" strahler="2">
<abba strahler="1">text3</abba>
<abbb strahler="2">
<abbba strahler="1">text4</abbba>
<abbbb strahler="1">text5</abbbb>
</abbb>
</abb>
</ab>
</a>
</root>
使用XSLT 3.0 / XPath 3.0的更紧凑,更高效的解决方案:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:f="my:f">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[true()]">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:attribute name="strahler" select="f:strahler(.)"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:function name="f:strahler" as="xs:integer" cache="full">
<xsl:param name="pElem" as="element()"/>
<xsl:sequence select=
"let $children := $pElem/*
return
if(not($children))
then 1
else
if(not($children[2]))
then f:strahler($children[1])
else
let $childrenStrahler := $children/f:strahler(.),
$maxVal := max($childrenStrahler),
$twoWithMax := ($childrenStrahler[. eq $maxVal][2]!1, 0)[1]
return $maxVal +$twoWithMax
"/>
</xsl:function>
</xsl:stylesheet>
<强>效率强>:
XSLT可能不是最佳工具......
如果有人告诉你,不要相信他们。
通过使用记忆,两种呈现的解决方案都是高效的。
事实上,f:stahler($aNode)
只为每个不同的元素计算一次。
请注意使用:
saxon:memo-function="yes"
cache="full"
第一个是Saxon's extension attributes之一。根据{{3}}的文档:
指定“是”表示撒克逊人应该记住结果 在缓存中调用该函数,以及是否再次调用该函数 使用相同的参数,结果将从缓存中检索 而不是重新计算。
第二个是 saxon:memo-function :
值cache =“full”鼓励处理器保留内存 以前在同一转换过程中调用此函数的所有函数 尽可能重用此内存中的结果。价值 cache =“partial”鼓励处理器保留这样的内存但是 如果需要,丢弃结果以保持其中使用的内存量 界限。默认值cache =“no”鼓励处理器不要 保留以前通话的记忆。
答案 1 :(得分:1)
XSLT适合这项工作,而Dimitre的解决方案非常完美(+1)。
以下是XQuery 1.0中的替代实现:
declare function local:strahler($nodes as node()*) as node()*
{
for $node in $nodes
return
typeswitch ($node)
case document-node() return
document {local:strahler($node/node())}
case element() return
element {node-name($node)}
{
let $children := local:strahler($node/node())
let $max := (max($children/@strahler), 1)[1]
return
(
$node/@*,
attribute strahler {$max + (($children/@strahler[. = $max])[2]/1, 0)[1]},
$children
)
}
default return
$node
};
local:strahler(.)
当使用strahler.xq
中的输入放入文件sample.xml
时,可以使用Saxon使用此命令从命令行运行它:
java net.sf.saxon.Query strahler.xq -s:sample.xml
与XSLT变体类似,它在自下而上添加新属性的同时重写文档。这里的重写比使用XSLT更明显,但在这种情况下,这是非常充分的。
答案 2 :(得分:0)
XSLT可能不是最佳工具,主要是因为它无法从下往上真正遍历树。 可以将Strahler数一次添加到树中一层,从最深层开始 - 但每次都必须重新处理整个树,导致算法非常低效。
但是,如果没有其他方法可用,你可以尝试这样的事情:
XSLT 2.0
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:variable name="max-depth" select="max(//*/count(ancestor::node()))"/>
<xsl:template match="/">
<xsl:call-template name="strahler">
<xsl:with-param name="node-set" select="*"/>
<xsl:with-param name="current-level" select="$max-depth"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="strahler">
<xsl:param name="node-set" select="*"/>
<xsl:param name="current-level"/>
<xsl:variable name="next-set">
<xsl:apply-templates select="$node-set" mode="strahler">
<xsl:with-param name="current-level" select="$current-level"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:choose>
<xsl:when test="$current-level > 1">
<!-- recursive call -->
<xsl:call-template name="strahler">
<xsl:with-param name="node-set" select="$next-set"/>
<xsl:with-param name="current-level" select="$current-level - 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="$next-set"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="*" mode="strahler">
<xsl:param name="current-level"/>
<xsl:copy>
<xsl:if test="count(ancestor::node()) = $current-level">
<xsl:attribute name="strahler">
<xsl:variable name="max-strahler" select="max(*/@strahler)" />
<xsl:choose>
<xsl:when test="not(*)">
<xsl:value-of select="1"/>
</xsl:when>
<xsl:when test="count(*[@strahler=$max-strahler]) > 1">
<xsl:value-of select="$max-strahler + 1"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$max-strahler"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
</xsl:if>
<xsl:copy-of select="@*"/>
<xsl:apply-templates mode="strahler">
<xsl:with-param name="current-level" select="$current-level"/>
</xsl:apply-templates>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
应用于您的输入示例,结果将是:
<?xml version="1.0" encoding="UTF-8"?>
<root strahler="3" field="4">
<a strahler="3">
<aa strahler="1" x="1"/>
<ab strahler="3">
<aba strahler="2" number="36" usefulness="useful">
<abaa strahler="1">text1</abaa>
<abab strahler="1">
<ababa strahler="1">text2</ababa>
</abab>
</aba>
<abb strahler="2" number="37" usefulness="useful">
<abba strahler="1">text3</abba>
<abbb strahler="2">
<abbba strahler="1">text4</abbba>
<abbbb strahler="1">text5</abbbb>
</abbb>
</abb>
</ab>
</a>
</root>
这与您发布的结果不同,但我认为根据您链接到的维基百科文章中的规则,这是正确的。
注意&#34;身高&#34;在这里没用。