Skip to content

Instantly share code, notes, and snippets.

@mattio
Created May 28, 2021 15:54
Show Gist options
  • Save mattio/7332cdbce988fe992a801141210a92bc to your computer and use it in GitHub Desktop.
Save mattio/7332cdbce988fe992a801141210a92bc to your computer and use it in GitHub Desktop.
This is some crazy old code from back in the day
xquery version "1.0";
module namespace r = "http://mattiovalentino.com/xquery/roman/";
(: Thanks to Marko Schulz for seeding this idea with his JavaScript version at http://vimeo.com/16935085. :)
declare function r:roman( $input as xs:string ) as xs:integer
{
(: I'd like to fail fast here if the string is empty. What's the best way? :)
let $characters as xs:string* := r:convert-string-to-characters(upper-case($input))
let $numbers as xs:integer* :=
for $i in $characters
return
r:convert-string-to-integer($i)
let $total as xs:integer* :=
for $i at $count in $numbers
return
if($i lt $numbers[$count + 1]) then (-$i) (: Handles subtractive principle of Roman numerals. :)
else $i
return sum($total)
};
(: Saxon didn't seem to like my use of local: here. Why? What am I missing? :)
declare function r:convert-string-to-integer( $input as xs:string ) as xs:integer
{
(: Seems like a hacky way to implement a switch-like statement. Is there a better way?
My earlier version was for $i in 1 to 1 return ...
A reviewer suggested two sequences and then ($numbers[index-of($letters, 'I')], 0)[1] :)
let $results as xs:integer+ :=
if($input eq "I") then 1
else if($input eq "V") then 5
else if($input eq "X") then 10
else if($input eq "L") then 50
else if($input eq "C") then 100
else if($input eq "D") then 500
else if($input eq "M") then 1000
else 0
return $results
};
(: From http://www.xqueryfunctions.com/xq/functx_chars.html :)
declare function r:convert-string-to-characters( $input as xs:string ) as xs:string*
{
for $c in string-to-codepoints($input)
return codepoints-to-string($c)
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment