Prolog将数字转换为罗马数字

5
我有一个将整数转换为罗马数字的代码。 我需要添加一个功能,将整数与输入的罗马数字进行比较,并显示它是真还是假。例如: roman(v,5)。 真。
toroman(0).
toroman(N) :- N < 4, put("I"), M is N - 1, toroman(M).
toroman(N) :- N = 4, put("I"), put("V").
toroman(N) :- N = 5, put("V").
toroman(N) :- N < 9, put("V"), M is N - 5, toroman(M).
toroman(N) :- N = 9, put("I"), put("X").
toroman(N) :- N < 40, put("X"), M is N - 10, toroman(M).
toroman(N) :- N < 50, put("X"), put("L"), M is N - 40, toroman(M).
toroman(N) :- N < 90, put("L"), M is N - 50, toroman(M).
toroman(N) :- N < 100, put("X"), put("C"), M is N - 90, toroman(M).
toroman(N) :- N < 400, put("C"), M is N - 100, toroman(M).
toroman(N) :- N < 500, put("C"), put("D"), M is N - 400, toroman(M).
toroman(N) :- N < 900, put("D"), put("D"), M is N - 500, toroman(M).
toroman(N) :- N < 1000, put("C"), put("M"), M is N - 900, toroman(M).
toroman(N) :- N < 4000, put("M"), M is N - 1000, toroman(M).



roman(N) :- toroman(N).

2
尝试输入 toroman(-1):你会得到 IVXXLXCCDCM。这与 toroman(0+9) 的值相同。 - false
2个回答

4
尝试以不同方式表述问题:编写一个语法(),将整数和表示罗马数字的字符列表相关联。以下是一个开端:
:- use_module(library(clpfd)).
roman(0) --> "". roman(N0) --> "I", { 1 #=< N0, N0 #=< 3, N1 #= N0-1}, roman(N1).
您可以像这样使用它:
?- phrase(roman(3), L).
   L = "III"
;  false.
或者
?- phrase(roman(N), "II").
   N = 2
;  false.
或者,如果您不知道该问什么,请简单地询问最一般的问题:
?- phrase(roman(N), L).
   N = 0, L = []
;  N = 1, L = "I"
;  N = 2, L = "II"
;  N = 3, L = "III"
;  false.
要像L = "III"这样简洁地获得答案,使用:- set_prolog_flag(double_quotes,chars).。有关详细信息,请参见this answer

4
使用CLP(FD)在DCG中表达语义似乎非常有趣! - CapelliC
3
@chac:SWI 中的 CLP(FD) 实际上是作为整数算术运算更纯粹的替代品。 - false
3
它提供了如此高级别的结构,至少在实际中不仅仅是算术。 - CapelliC
2
@chac:大多数CLP(FD)实现存在严重的正确性和其他限制,使它们无法作为替代品使用。 - false
3
是的,在普通 Prolog 中有 CLP(FD) 很棒。谢谢 Markus! - CapelliC
显示剩余3条评论

0

你应该把你的 toroman/1 过程改成像 toroman/2 那样返回罗马数字而不仅仅是将其打印出来。

这样,你就可以轻松地将罗马数字与调用toroman/2返回的整数进行比较。

另外请注意,如果你回溯寻找其他解决方案,你目前的过程将一直循环直到出现堆栈溢出。你应该为每个递归调用自身的条款加上保护条件,只有在调用的参数为非负数时才进行递归,或者添加一个安全失败检查作为第一个条款,例如:

roman(N):- N < 0, !, fail.

将toroman/1更改为返回罗马字面值,您将得到类似于以下内容的代码(仅稍微修改了一下您的代码以将文字作为第二个参数返回):

toroman(N, _):- N < 0, !, fail.
toroman(0, []).
toroman(N, ['I'|Roman]) :- N < 4, M is N - 1, toroman(M, Roman).
toroman(4, ['IV']).
toroman(5, ['V']).
toroman(N, ['V'|Roman]) :- N < 9, M is N - 5, toroman(M, Roman).
toroman(9, ['IX']).
toroman(N, ['X'|Roman]) :- N < 40, M is N - 10, toroman(M, Roman).
toroman(N, ['XL'|Roman]) :- N < 50, M is N - 40, toroman(M, Roman).
toroman(N, ['L'|Roman]) :- N < 90, M is N - 50, toroman(M, Roman).
toroman(N, ['XC'|Roman]) :- N < 100, M is N - 90, toroman(M, Roman).
toroman(N, ['C'|Roman]) :- N < 400, M is N - 100, toroman(M, Roman).
toroman(N, ['CD'|Roman]) :- N < 500, M is N - 400, toroman(M, Roman).
toroman(N, ['DD'|Roman]) :- N < 900, M is N - 500, toroman(M, Roman).
toroman(N, ['CM'|Roman]) :- N < 1000, M is N - 900, toroman(M, Roman).
toroman(N, ['M'|Roman]) :- N < 4000, M is N - 1000, toroman(M, Roman).

roman(N, R) :- toroman(N, L), atomic_list_concat(L, R).

roman(N) :- roman(N, R), write(R).

然后,您可以简单地调用roman(N, R)并测试R是否与您感兴趣的罗马数字相一致。


谢谢,这真的很有帮助。列表就是我需要的。我试图修改您的代码以便使用类似以下问题的 true 或 false 结果:?- roman(5,V)。 true。?- roman(5,R)。 R=V。我应该改变什么? - Muhsag
你必须将罗马字面值用单引号括起来(因为你想使用原子,而不是变量)。因此,例如,你可以问?- roman(5, 'V')。 - gusbro
我明白了,只需要将所有大写字母改为小写即可。 - Muhsag

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接