In my first attempt, I had written a couple of templates to "hash" or "digitize" a string. I named them after the functions in Zhanyong Wan's Haskell code:
<xsl:template name="charHash">
<!-- converts a character to its corresponding digit in the keypad -->
<xsl:param name="ch"/>
<xsl:value-of select="substring($keypad/key[contains(., $ch)]/., 1, 1)"/>
</xsl:template>
<xsl:template name="strHash">
<!-- converts a string to its corresponding digit sequence -->
<xsl:param name="str"/>
<xsl:param name="i" select="1"/>
<xsl:call-template name="charHash">
<xsl:with-param name="ch" select="substring($str, $i, 1)"/>
</xsl:call-template>
<xsl:if test="$i < string-length($str)">
<xsl:call-template name="strHash">
<xsl:with-param name="str" select="$str"/>
<xsl:with-param name="i" select="$i + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
The $keypad in the "charHash" template pointed to a keypad element in my input file:
<keypad>
<key>2abc</key>
<key>3def</key>
<key>4ghi</key>
<key>5jkl</key>
<key>6mno</key>
<key>7pqrs</key>
<key>8tuv</key>
<key>9wxyz</key>
</keypad>
At first, I was satisfied with the code, because it worked, exactly as planned. Until I figured that I could also use XSLT's built-in translate function:
translate($string, 'abcdefghijklmnopqrstuvwxyz', '22233344455566677778889999')
One (simple and clear) line of code replaced the two templates and keypad element. It does exactly the same thing.