XSLTに計算をさせてみる
とりあえず、階乗を出力するXSLTを書いてみた。
<?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>factorial</title> </head> <body> <p> <xsl:apply-templates select="factorial"/> </p> </body> </html> </xsl:template> <!-- 階乗要素から階乗を計算する --> <xsl:template match="factorial"> <xsl:call-template name="factorial-impl"> <xsl:with-param name="n" select="@n"/> </xsl:call-template> </xsl:template> <!-- 階乗の計算をするテンプレート --> <xsl:template name="factorial-impl"> <!-- 計算する値 --> <xsl:param name="n"/> <!-- 途中結果 デフォルト値は1 --> <xsl:param name="r" select="1"/> <!-- xsl:chooseを使うとインデントが深くなるので、xsl:ifを使用 --> <!-- 終了条件 --> <xsl:if test="$n=0"> <xsl:value-of select="$r"/> </xsl:if> <!-- 計算本体 --> <xsl:if test="$n!=0"> <xsl:call-template name="factorial-impl"> <xsl:with-param name="n" select="$n - 1"/> <xsl:with-param name="r" select="$n * $r"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet>
計算結果をxsl:variableで受け取ってもいいけど、無意味に末尾再帰にしてみた。
XSLTでは分岐のためにxsl:ifとxsl:chooseがあって、xsl:ifは単一の分岐に、xsl:chooseは複数の分岐に使用する。
でも、xsl:elseはないから、成立する場合としない場合に別の処理をさせたい場合、xsl:chooseを使うことが良くある。
上のxsl:ifをxsl:chooseで書き換えると、
<xsl:choose> <xsl:when test="$n!=0"> <xsl:value-of select="$r"/> </xsl:when> <xsl:otherwise> <xsl:call-template name="factorial-impl"> <xsl:with-param name="n" select="$n - 1"/> <xsl:with-param name="r" select="$n * $r"/> </xsl:call-template> </xsl:otherwise> </xsl:choose>
こんな感じ。
コメントにあるように、インデントが深くなってしまうのが難点。
ということで、実際には真偽が反対になるxsl:ifを書くことでelseっぽいものを実現してみたり。
=に対して!=の他にも、全体をnot()に渡す方法もある。
<?xml version='1.0' encoding='utf-8'?> <?xml-stylesheet href='factorial.xsl' type='text/xsl'?> <factorial n="170"/>
72574156153080040000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000000
これ以上はInfinityが出力された。てか計算速いな。もしかして末尾最適化が実装されてる?
*1:見にくいので80文字で折り返し