A lógica da numeração romana é curiosa porque não se baseia em nenhuma base, embora o 5 e o 10 tenham um papel tipo base.
Outro aspecto interessante da sua lógica é que obedece a um padrão greedy. Como tal podem-se usar expressões regulares para converter de/para romano. Entra o sed
.
conversão de romano para decimal
#! /bin/sed -f
# fr_roman.sed -- from roman (to decimal)
# Carlos Duarte, 980707
# usage: echo MCMXCVIII | sed -f fr_roman.sed
# output: 1998
#
# converts roman literals to decimal
# some input error checking is done, but not all cases are covered
# (bigger constructions following smallers, for instance)
#
# build table, all possible constructs that evaluate to [1-9]0* are here
1{
x
s/$/0/
s/$/I1/
s/$/II2/
s/$/III3/
s/$/IV4/
s/$/V5/
s/$/VI6/
s/$/VII7/
s/$/VIII8/
s/$/IX9/
s/$/X10/
s/$/XX20/
s/$/XXX30/
s/$/XL40/
s/$/L50/
s/$/LX60/
s/$/LXX70/
s/$/LXXX80/
s/$/XC90/
s/$/C100/
s/$/CC200/
s/$/CCC300/
s/$/CD400/
s/$/D500/
s/$/DC600/
s/$/DCC700/
s/$/DCCC800/
s/$/CM900/
s/$/M1000/
s/$/MM2000/
s/$/MMM3000/
x
}
# converts from MMXXII to 2000,20,2, from lookup table
s/^/\
/
G
ta
:a
s/\n\(..*\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3,\
\2\1\3/
tb
s/\n/>>> error:/
s/\n.*//
#q
b
:b
/\n\n/!ba
s/\n\n.*$//
# convert from 2000,20,2, to 2022. this is not trivial!
#
# steps:
# 2000,20,2, -> 2000<20>2, <20> is the work digit
:d
s/,/</
te
:e
s/,/>/
tf
# exit point here: contains `2022,' so, remove the `,' and go
s/.$//
b
# 2000<20>2, -> 2000,:20<20>2, : is the cursor for changing the 20 in ,:20
# into 00 (for later, remove these from 2000)
:f
s/<\(.*\)>/,:\1&/
# 2000,:20<20>2, -> 2000,00:<20>2,
tc
:c
s/:[0-9]/0:/
tc
# 2000,00:<20>2, -> 2020,2, convert next digit
s/\(0*\),\1:<//
s/>/,/
bdconversão de decimal para romano
#! /bin/sed -f
# to_roman.sed -- converts decimal to roman
# Carlos Duarte, 980707
# do not perform error checking
# converts input treated as arabic decimals, to roman literal
#
# eg: echo 1998 | sed -f to_roman.sed
# outputs: MCMXCVIII
#
# build table, all possible constructs that evaluate to [1-9]0* are here
1{
x
s/$/1I/
s/$/2II/
s/$/3III/
s/$/4IV/
s/$/5V/
s/$/6VI/
s/$/7VII/
s/$/8VIII/
s/$/9IX/
s/$/10X/
s/$/20XX/
s/$/30XXX/
s/$/40XL/
s/$/50L/
s/$/60LX/
s/$/70LXX/
s/$/80LXXX/
s/$/90XC/
s/$/100C/
s/$/200CC/
s/$/300CCC/
s/$/400CD/
s/$/500D/
s/$/600DC/
s/$/700DCC/
s/$/800DCCC/
s/$/900CM/
s/$/1000M/
s/$/2000MM/
s/$/3000MMM/
x
}
s/.*/:&:/
ta
:a
s/:.\([^:]*\):$/&\1:/
ta
:c
s/:\(.\)/\1,/
tb
:b
s/,[0-9]/0,/
tb
/,::$/bd
s/,/;/
bc
:d
s/...$/;/
s/;00*//g
s/^/,/
G
te
:e
s/,\([^;]*\);\(.*\n.*\)\1\([IVXLCDM][IVXLCDM]*\)/\3,\2\1\3/
te
s/.\n.*//compilador de romano para expressão numérica do equivalente decimal
echo MCXLIII | sed -f from_roman.sed
=> 1000+100+40+1+1+1echo MCMLXXXI | sed -f from_roman.sed
=> 1000+900+50+10+10+10+1
#! /bin/sed -f
# from_roman.sed -- output bc(1) code that convert roman to decimal
# Carlos Duarte, 980707
# usage: echo MCXLIII | sed -f from_roman.sed | bc
s/$/\
0M1000CM900D500CD400C100XC90L50XL40X10IX9V5IV4I1/
s/^/\
/
ta
:a
s/\n\(..\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3+\
\2\1\3/
tb
s/\n\(.\)\(.*\n.*[0-9]\)\1\([1-9]0*\)/\3+\
\2\1\3/
tb
s/\n/0; ">>> error:/
s/\n.*/"/
#q
b
:b
/\n\n/!ba
s/.\n\n.*$//