Math 将罗马数字翻译成阿拉伯数字

Math 将罗马数字翻译成阿拉伯数字,math,integer,prolog,converter,roman-numerals,Math,Integer,Prolog,Converter,Roman Numerals,我构建了一个谓词,它将罗马数字转换为阿拉伯数字。唯一的问题是谓词是有限的:如果我想一次转换3个以上的阿拉伯数字,它就不起作用了 谓词应该是这样工作的: ?- convert([v,i,i],Arabic). Arabic = 7. 到目前为止,我的解决方案是: tran([],0). tran(i,1). tran(v,5). tran(x,10). convert([],X) :- X is 0, !. convert([T],X) :- tran(T,E), X is E,!. conv

我构建了一个谓词,它将罗马数字转换为阿拉伯数字。唯一的问题是谓词是有限的:如果我想一次转换3个以上的阿拉伯数字,它就不起作用了

谓词应该是这样工作的:

?- convert([v,i,i],Arabic).
Arabic = 7.
到目前为止,我的解决方案是:

tran([],0).
tran(i,1).
tran(v,5).
tran(x,10).

convert([],X) :- X is 0, !.
convert([T],X) :- tran(T,E), X is E,!.
convert([T|Ts],X) :- tran(T,E), tran(Ts,Es), X is E+Es,!.
convert([T,Ts,Tss],X) :- tran(T,E), tran(Ts,Es), tran(Tss,Ess), X is E+Es+Ess.
我知道为什么谓词不能处理3个以上的数字,我也可以扩展convert谓词,但使用与上面所示相同的模式

如何使convert谓词更“通用”(这样它就可以独立于数字的数量工作)?或者你对如何编写谓词有其他想法吗?
谢谢:)

我没有太多的测试,但我已经在几个数字上尝试过了,而且似乎很有效

代码遵循“减法对规则”,例如,在

该代码使用“累加器”技术传递信息,即以前看到的数字总和。初始调用只是将累加器设置为0

digit(i, 1).
digit(v, 5).
digit(x, 10).
digit(l, 50).
digit(c, 100).
digit(d, 500).
digit(m, 1000).

convert(Roman, Arabic) :-
    convert(Roman, 0, Arabic).

convert([], Acc, Acc).

convert([A], Acc, Arabic) :-
    digit(A, AVal),
    Arabic is Acc + AVal.

convert([A, B | Rest], Acc, Arabic) :-
    digit(A, AVal), digit(B, BVal),
    AVal < BVal,
    NewAcc is Acc + BVal - AVal,
    convert(Rest, NewAcc, Arabic).

convert([A, B | Rest], Acc, Arabic) :-
    digit(A, AVal), digit(B, BVal),
    AVal >= BVal,
    NewAcc is Acc + AVal,
    convert([B | Rest], NewAcc, Arabic).

<> p>可以用约束编程编写一个谓词<代码>转换< /COD>两种方法,但是我没有尝试过这种方法。

如果你认为罗马数字系统中离散的“数字”的数目不仅仅是I、X和V,则可以帮助,即:

roman( "M"   , 1000 ) .
roman( "CM"  ,  900 ) .
roman( "D"   ,  500 ) .
roman( "CD"  ,  400 ) .
roman( "C"   ,  100 ) .
roman( "XC"  ,   90 ) .
roman( "L"   ,   50 ) .
roman( "XL"  ,   40 ) .
roman( "X"   ,   10 ) .
roman( "IX"  ,    9 ) .
roman( "V"   ,    5 ) .
roman( "IV"  ,    4 ) .
roman( "I"   ,    1 ) .
然后你可以写一些像

roman_to_decimal( R , D ) :-
  roman_to_decimal( R , 0 , D )
  .

roman_to_decimal( [] , D , D ) :- .
roman_to_decimal( R  , T , D ) :-
  roman(P,V) ,
  append(P,S,R) ,
  ! ,
  T1 is T+V ,
  roman_to_decimal(S,T1,D)
  .
把它当作

roman_to_decimal( "MCM" , D ) .
这确实有一些缺点,具体来说:

  • 它不强制语法:罗马编号系统要求离散组件按值的降序从左到右排序。这并没有考虑到这一点

  • 它没有考虑到许多变化。999应该表示为紧凑的IM还是更详细的CMXCIX


为了给混合添加一个变体,这个版本使用了Sergey答案中的方案(它还允许更多的任意减法序列),并允许像Nicholas的答案一样的更人性化的输入

numeral('I', 1).
numeral('V', 5).
numeral('X', 10).
numeral('L', 50).
numeral('C', 100).
numeral('D', 100).
numeral('M', 1000).

r2n(R, N) :-
    char_code(A, R),
    lower_upper(A, C),
    numeral(C, N).

trans(R, N) :-
    maplist(r2n, R, Rn),    % Pre-calculate a numeric list representation
    trans(Rn, 0, N).
trans([X,Y|T], Acc, N) :-
    X >= Y,
    Acc1 is Acc + X,
    trans([Y|T], Acc1, N).
trans([X,Y|T], Acc, N) :-
    X < Y,
    Acc1 is Acc - X,
    trans([Y|T], Acc1, N).
trans([X], Acc, N) :-
    N is Acc + X.
trans([], N, N).   % Optional rule: needed only if you want trans("", 0). to succeed

一些评论和提示。您可以将
转换([],X):-…
简化为
转换([],0)。
转换([T],X):-…
转换([T],E):-tran(T,E)。
请注意,罗马数字的规则(请参阅)取决于值越高,值越低。因此,您的
convert
谓词将需要查看两个连续的值来进行选择,例如
convert([X,Y | T],Value):-X
。我还建议首先将列表转换为数字,然后进行操作以简化算术和比较。这基本上就是我所考虑的方法。一个小的变化是在
convert/2
谓词中调用
maplist(digital,Roman,Numbers)
,然后在
Numbers
上运行
convert/3
,消除单个
digital/2
查询。严格来说,第二个项目不是问题,因为根据罗马数字的描述,像“IM”这样的表单实际上是无效的。在罗马数字减法中,只有少数几个具体的例子。实际的罗马用法比现代惯例允许的更灵活:“CCCCC”和“D”都是表示500的非常有效的方式。让事情复杂化的是,罗马全盛时期的用法还包括大值支持——一个“数字”上的宏(水平条)按1000(M-with-macron=1000000,V-with-macron=5000,等等)缩放。规则性是用于WIMP的!酷,我不知道。我比较喜欢这种方法。还有两条注释:(1)需要大写字母
罗马到十进制([],D,D)。
,(2)
罗马(S,T1,D)
应该是
罗马到十进制(S,T1,D)
我假设,(3)它需要一个切分,或者
罗马到十进制(“MCM,D”)
将产生
1900
2100
作为解决方案。拼写错误已修复。我记得高中拉丁语课上的一些内容:D
numeral('I', 1).
numeral('V', 5).
numeral('X', 10).
numeral('L', 50).
numeral('C', 100).
numeral('D', 100).
numeral('M', 1000).

r2n(R, N) :-
    char_code(A, R),
    lower_upper(A, C),
    numeral(C, N).

trans(R, N) :-
    maplist(r2n, R, Rn),    % Pre-calculate a numeric list representation
    trans(Rn, 0, N).
trans([X,Y|T], Acc, N) :-
    X >= Y,
    Acc1 is Acc + X,
    trans([Y|T], Acc1, N).
trans([X,Y|T], Acc, N) :-
    X < Y,
    Acc1 is Acc - X,
    trans([Y|T], Acc1, N).
trans([X], Acc, N) :-
    N is Acc + X.
trans([], N, N).   % Optional rule: needed only if you want trans("", 0). to succeed
| ?- trans("mmxiv", X).

X = 2014 ? ;

no
| ?- trans("CMXCIX", X).

X = 999 ? ;

no
| ?- trans("IM", X).

X = 999 ? ;

no
| ?- trans("IVX", X).   % Not a properly-formed Roman numeral

X = 4 ? ;   % Uh... ok... I guess

no