XSLT 4.0 Proposal:タプル

非常に複雑な処理を行うテンプレート、関数を作っていて、戻り値として複数の値を呼び出し側に返したいと考えたことはないでしょうか?私は大いにありました.これをXSLT 2.0まででやっていた時代には次のような方法がありました.

 

  • テンポラリツリーを作って、要素/属性の組み合わせで返す.
  • as="item()*"のような戻り値の定義をしてシーケンスとして返す.

 

両方ともエレガントではありません.XSLT 1.0で必死にやろうとすれば前者.XSLT 2.0でやろうとすれば後者ではないかと思います.

 

例えば以下の関数は、要素を与えて、要素名、@id属性、ツリーの中での階層を返します.(手間なのでXSLT 3.0でやっています.)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:ahf="http://www.antennahouse.com/names/function"
exclude-result-prefixes="xs math"
version="3.0">

<xsl:function name="ahf:getElementInfo" as="item()*">
<xsl:param name="prmElem" as="element()"/>
<xsl:sequence select="name($prmElem), $prmElem/@id, $prmElem/ancestor-or-self::* => count()"/>
</xsl:function>

<xsl:template match="*">
<xsl:variable name="elementInfo" as="item()*" select=". => ahf:getElementInfo()"/>
<xsl:message select="'name=' || $elementInfo[1] || ' @id=' || $elementInfo[2] => string() || ' level=' || $elementInfo[3] => string()"/>
<xsl:apply-templates select="*"/>
</xsl:template>

</xsl:stylesheet>

これで十分動いてくれるのですが、呼び出し側と関数との間には暗黙に戻り値のシーケンスの1番目は要素名(xs:string)、2番目は@id属性(attribute())、3番目はその要素の階層レベル(xs:integer)でやり取りするという約束事を作っておく必要があります.

 

このスタイルシートに次のような入力を与えると

<?xml version="1.0" encoding="UTF-8"?>
<doc id="id0001">
<chapter id="id0002">
<section id="id0003">
<p id="id0004">A paragraph</p>
</section>
</chapter>
</doc>

結果は

 

name=doc @id=id0001 level=1

name=chapter @id=id0002 level=2

name=section @id=id0003 level=3

name=p @id=id0004 level=4

 

と表示してくれます.しかし戻り値を返すのはもっとスマートにやりたいものです.これを実現してくれるのがタプル(tuple)です.

 

A Proposal for XSLT 4.0では、このタプルを新しい型として追加する提案がなされています.実はSaxonはすでに独自実装でタプルを提供しています.タプルを使えば、[n]のような参照の仕方でなく、タプルのフィールド名で参照することができます.(タプル型の変数?フィールド名)

 

次のようになるでしょう.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:math="http://www.w3.org/2005/xpath-functions/math"
xmlns:ahf="http://www.antennahouse.com/names/function"
xmlns:saxon="http://saxon.sf.net/"
exclude-result-prefixes="xs math"
version="3.0">

<xsl:function name="ahf:getElementInfo" saxon:as="tuple(name: xs:string, id: attribute()?, level: xs:integer)">
<xsl:param name="prmElem" as="element()"/>
<xsl:sequence select="map{'name': name($prmElem), 'id': $prmElem/@id, 'level': $prmElem/ancestor-or-self::* => count()}"/>
</xsl:function>

<xsl:template match="*">
<xsl:variable name="elementInfo" as="item()*" select="ahf:getElementInfo(.)"/>
<xsl:message select="'name=' || $elementInfo?name || ' @id=' || $elementInfo?id => string() || ' level=' || $elementInfo?level => string()"/>
<xsl:apply-templates select="*"/>
</xsl:template>

</xsl:stylesheet>
 

ここで注目される点はタプルの実体は実はマップ(map)であるということです.ですのでKay博士はProposalの中で次のように述べています.

Note that I'm not introducing tuples as a new kind of object here. The values

are still maps, and the set of operations that apply to tuples are exactly the same as the operations that apply to maps. I'm only introducing a new way of describing and constraining the type.

今作っているスタイルシートgrepしてみたら、すでに20か所以上でタプルを使っていました.実績は十分というよりもはやタプルはXSLTスタイルシート開発に必須の型に思えます.