[xquery-talk] XPath/XQuery equivalent of xsl:number?

G. Ken Holman gkholman at CraneSoftwrights.com
Thu Nov 17 16:53:14 PST 2011

At 2011-11-17 19:14 -0500, Michael Sokolov wrote:
>You might also someday be interested in the inverse function (roman 
>numeral to decimal); here is what we use.  It might be interesting 
>for the group to compare the iterative approach here with the 
>recursive approach to a similar problem in Ken's version?

Interesting thought, Mike!

>declare namespace ifp="http://www.ifactory.com/press";
>define function ifp:roman-number-to-decimal ($roman-number as xs:string)
>   let $roman-tokens := <roman-tokens>
><tok sym='M'>1000</tok>
><tok sym='D'>500</tok>
><tok sym='C'>100</tok>
><tok sym='L'>50</tok>
><tok sym='X'>10</tok>
><tok sym='V'>5</tok>
><tok sym='I'>1</tok>
>   let $l := string-length($roman-number)
>   return  sum (
>     for $i in (0 to $l - 1)
>       let $t := substring($roman-number, $l - $i, 1)
>       let $s := substring($roman-number, $l - $i + 1, 1)
>       let $tv := number($roman-tokens/tok[@sym=$t])
>       let $sv := number($roman-tokens/tok[@sym=$s])
>     return if ($sv and $sv gt $tv) then $tv * -1 else number($tv)
>   )

I just modified my module as below in order to offer the inverse of 
Roman numeral to decimal, using the same lookup table as I use for 
the initial direction.  Interestingly, the solution has very much the 
same shape!

. . . . . . . . . . . . Ken

xquery version "1.0";

   A library to transform a number less than 4000 to a sequence of 
Roman digits.

   Crane Softwrights Ltd. XQuery Training

module namespace n2r = "urn:X-Crane:n2roman";

(:the basis of transformation is a series of strings for components:)
declare variable $n2r:values as element(value)+ :=
   <value num="1"    char="I" />,
   <value num="4"    char="IV"/>,
   <value num="5"    char="V" />,
   <value num="9"    char="IX"/>,
   <value num="10"   char="X" />,
   <value num="40"   char="XL"/>,
   <value num="50"   char="L" />,
   <value num="90"   char="XC"/>,
   <value num="100"  char="C" />,
   <value num="400"  char="CD"/>,
   <value num="500"  char="D" />,
   <value num="900"  char="CM"/>,
   <value num="1000" char="M" />

(:return the concatenation of strings by continuous reduction:)
declare function n2r:n2roman ( $num as xs:integer ) as xs:string
   (:as long as we have a number, keep going:)
   if ( $num ) then
     (:reduce by the largest number that has a string value:)
     for $val in $n2r:values[@num <= $num][fn:last()] return
       (:using the highest value:)
       fn:concat( $val/@char,n2r:n2roman( $num - xs:integer( $val/@num ) ) )
   (:nothing left:)
   else ""

(:return the sum of numbers by continuous addition:)
declare function n2r:roman2n ( $str as xs:string ) as xs:integer
   (:as long as we have a string, keep going:)
   if ( $str ) then
     (:reduce by the largest number that has a string value:)
     for $val in $n2r:values[starts-with($str, at char)][fn:last()] return
       (:using the highest value:)
       xs:integer( $val/@num ) + n2r:roman2n(substring-after($str,$val/@char))
   (:nothing left:)
   else 0

(:test all numbers up to but not including 4000:)
declare function n2r:test ( ) as xs:integer*
   for $each in 1 to 3999 return if( $each != n2r:roman2n(n2r:n2roman($each)) )
                                 then $each else ()

(:end of file:)

Contact us for world-wide XML consulting and instructor-led training
Free 5-hour video lecture: XSLT/XPath 1.0 & 2.0 http://ude.my/t37DVX
Crane Softwrights Ltd.            http://www.CraneSoftwrights.com/q/
G. Ken Holman                   mailto:gkholman at CraneSoftwrights.com
Google+ profile: https://plus.google.com/116832879756988317389/about
Legal business disclaimers:    http://www.CraneSoftwrights.com/legal

More information about the talk mailing list