我构建了一个谓词,将罗马数字转换为阿拉伯数字。唯一的问题是谓词是有限的:如果我想一次转换超过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-predicate更“通用”(这样它可以独立于数字的数量工作)?或者你有其他想法如何编写谓词? 谢谢:))
答案 0 :(得分:3)
我没有对此进行太多测试,但我已经尝试了几个数字,它似乎有效。
代码服从“减法对规则”,例如在https://projecteuler.net/about=roman_numerals
中描述代码使用“累加器”技术传递信息,这是以前看到的数字之和。初始调用只是将累加器设置为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).
一些测试:
convert([v, i, i], Arabic).
Arabic = 7
?- convert([x, i, x], Arabic).
Arabic = 19
?- convert([m, d, c, v, i], Arabic).
Arabic = 1606
可能编写一个谓词convert
,它使用约束编程在真正的Prolog精神中有两种方式,但我没有尝试过这种方法。
答案 1 :(得分:3)
如果您认为罗马编号系统中离散“数字”的数量不仅仅是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 ?
答案 2 :(得分:2)
只是为混音添加一个变体,这个版本在谢尔文的答案中使用了这个方案(它也允许更多的任意减法序列),并允许更像人类可读的输入,如尼古拉斯的答案。
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