整数与其名称之间的关系 - Prolog

时间:2014-04-25 17:09:13

标签: numbers prolog dcg

我正试图在Prolog中探索自然数,Peano数,算术等概念。现在我正在尝试创建一个谓词,允许我输入任何数字的名称并获得它的数值(反之亦然)。我该怎么做?我的想法是我会翻译给定的数字,然后使用加号函数将它们加在一起(例如,一百四十五:一百,一百= 100,= 0,四十= 40,五= 5 - > 100 + 0 + 40 + 5 = 145.

以下是一些查询的示例:

?- numnames(X, 375).
X = [three, hundred, and, seventy, five] ;
false.
?- numnames([seven], 7).
true ;
false.

以下是我的一些详尽的事实(我刚刚选择了一些可以解决特定类别的事实):

numnames([],0).
numnames(and,0).
numnames([one],1).
numnames([ninety],90).
numnames([one, hundred], 100).

我很困惑如何在算术之前翻译数字,以及我何时/何时停止制作详尽的事实并开始制定规则? 谢谢你的帮助。

3 个答案:

答案 0 :(得分:4)

这是Prolog 语法规则 DCGs 的一个很好的应用程序(基本上是隐藏一些列表操作的语法糖,并且对正常的Prolog规则有相对简单的翻译)。 / p>

num_0_999(0) --> [zero].
num_0_999(N) --> num_1_99(N).
num_0_999(N) --> num_100_999(N).

num_1_99(N) --> num_1_9(N).
num_1_99(N) --> num_10_19(N).
num_1_99(N) --> decade(T), opt_1_9(U), {N is T+U}.

num_100_999(N) --> num_1_9(H), [hundred], opt_1_99(S), {N is 100*H+S}.

opt_1_9(0) --> [].
opt_1_9(N) --> num_1_9(N).

opt_1_99(0) --> [].
opt_1_99(N) --> [and], num_1_99(N).

num_1_9(1) --> [one].   num_1_9(2) --> [two].   num_1_9(3) --> [three].
num_1_9(4) --> [four].  num_1_9(5) --> [five].  num_1_9(6) --> [six].
num_1_9(7) --> [seven]. num_1_9(8) --> [eight]. num_1_9(9) --> [nine].

num_10_19(10) --> [ten].        num_10_19(11) --> [eleven].
num_10_19(12) --> [twelve].     num_10_19(13) --> [thirteen].
num_10_19(14) --> [fourteen].   num_10_19(15) --> [fifteen].
num_10_19(16) --> [sixteen].    num_10_19(17) --> [seventeen].
num_10_19(18) --> [eighteen].   num_10_19(19) --> [nineteen].

decade(20) --> [twenty].        decade(30) --> [thirty].
decade(40) --> [forty].         decade(50) --> [fifty].
decade(60) --> [sixty].         decade(70) --> [seventy].
decade(80) --> [eighty].        decade(90) --> [ninety].

这两种方式都有效(并且可以枚举所有数字):

?- phrase(num_0_999(46), Name).
Name = [forty, six]
Yes (0.00s cpu, solution 1, maybe more)

?- phrase(num_0_999(N), [forty, six]).
N = 46
Yes (0.00s cpu, solution 1, maybe more)

[我最初使用了#= / 2约束而不是is / 2来使代码在两种模式下都能正常工作,但@ CapelliC的帖子提醒说,通过移动可以在普通的Prolog中实现相同的效果算术到各自规则的末尾......]

答案 1 :(得分:3)

我有一个奢侈的解决方案,但在某些情况下可能是实用的。

假设您计划使用的最大数量不是太大,可能是1000或10000.而且您也不想花太多时间创建Prolog程序,不想处理角落情况并且害怕拼写错误(例如,因为你不像我一样是英语母语人士)。

然后,您可以使用其他工具生成Prolog程序(事实列表)。 Common Lisp有一个标准工具可以将数字转换为英文单词。让我们用它:

(defun generate-prolog (k)
  (loop for n from 1 to k do
   (format t "numnames([~a], ~a). ~%" 
           (substitute #\, #\Space 
                       (remove #\,
                               (substitute #\Space #\- (format nil "~R" n)) ) ) n) ) )
(generate-prolog 1000)

使用CLISP生成的Prolog程序的片段(其他Lisp实现可以使用美式拼写,不带'和'):

numnames([two,hundred,and,thirty,six], 236). 
numnames([two,hundred,and,thirty,seven], 237). 
numnames([two,hundred,and,thirty,eight], 238). 
numnames([two,hundred,and,thirty,nine], 239). 
numnames([two,hundred,and,forty], 240). 
numnames([two,hundred,and,forty,one], 241). 
numnames([two,hundred,and,forty,two], 242). 
numnames([two,hundred,and,forty,three], 243). 
numnames([two,hundred,and,forty,four], 244). 
numnames([two,hundred,and,forty,five], 245). 
numnames([two,hundred,and,forty,six], 246). 
numnames([two,hundred,and,forty,seven], 247). 
numnames([two,hundred,and,forty,eight], 248). 
numnames([two,hundred,and,forty,nine], 249). 
numnames([two,hundred,and,fifty], 250). 

答案 2 :(得分:0)

我将报告Ken Johnson提出的深思熟虑的DCG解决方案。这里有完整的代码,只需要很小的修改就可以使用(我在SWI-Prolog中测试过)。我修改了像ten_to_nine{*filter*}(13) --> [thir{*filter*}].这样的规则,我真的不知道如何处理。

:- module(dcg_numerals, [number//1]).

% Grammar for numbers, e.g.
% phrase(number(I),[two,hundred,and,fifty,six]).
% An astonishing characteristic of this code is that it's
% fully bidirectional. The expression
% phrase(number(256),Words)
% will instantiate Words = [two,hundred,and,fifty,six].
% What's more,
% phrase(number(I),Words)
% will eventually instantiate I and Words to all the numbers it knows.
%
% Ken Johnson 17-9-87

number(I) --> '1_to_999'(I).
number(I) --> '1000_to_999999'(I).

 '1_to_999'(I) --> '1_to_99'(I).
 '1_to_999'(I) --> '100_to_999'(I).

% Compound number with thousands

 '1000_to_999999'(I) --> thousands(I).

 '1000_to_999999'(I) --> thousands(K),'100_to_999'(Htu),
        {
                I is K + Htu
        }.

 '1000_to_999999'(I) --> thousands(K),[and],'1_to_99'(Tu),
        {
                I is K + Tu
        }.

% Thousands

thousands(I) --> '1_to_999'(K), [thousand],
        {
                I is K * 1000
        }.

% Compound number with hundreds, tens and units

 '100_to_999'(C) --> one_to_nine(H),[hundred],
        {
                C is 100 * H
        }.

 '100_to_999'(C) --> one_to_nine(H),[hundred],[and],'1_to_99'(Tu),
        {
                C is (100 * H) + Tu
        }.

% Complete number: a single word 1-9 or 10-19

 '1_to_99'(I) --> one_to_nine(I).
% '1_to_99'(I) --> ten_to_nine{*filter*}(I).
 '1_to_99'(I) --> ten_to_nine(I).
 '1_to_99'(I) --> multiple_of_ten(I).
 '1_to_99'(I) --> '20_to_99'(I).

% Compound number with tens and units

  '20_to_99'(I) --> multiple_of_ten(T), one_to_nine(U),
        {
                I is T + U
        }.

% Single words (terminal nodes)

one_to_nine(1) --> [one].
one_to_nine(2) --> [two].
one_to_nine(3) --> [three].
one_to_nine(4) --> [four].
one_to_nine(5) --> [five].
one_to_nine(6) --> [six].
one_to_nine(7) --> [seven].
one_to_nine(8) --> [eight].
one_to_nine(9) --> [nine].

/*
ten_to_nine{*filter*}(10) --> [ten].
ten_to_nine{*filter*}(11) --> [eleven].
ten_to_nine{*filter*}(12) --> [twelve].
ten_to_nine{*filter*}(13) --> [thir{*filter*}].
ten_to_nine{*filter*}(14) --> [four{*filter*}].
ten_to_nine{*filter*}(15) --> [fif{*filter*}].
ten_to_nine{*filter*}(16) --> [six{*filter*}].
ten_to_nine{*filter*}(17) --> [seven{*filter*}].
ten_to_nine{*filter*}(18) --> [eigh{*filter*}].
ten_to_nine{*filter*}(19) --> [nine{*filter*}].
*/
ten_to_nine(10) --> [ten].
ten_to_nine(11) --> [eleven].
ten_to_nine(12) --> [twelve].
ten_to_nine(13) --> [thirteen].
ten_to_nine(14) --> [fourteen].
ten_to_nine(15) --> [fifteen].
ten_to_nine(16) --> [sixteen].
ten_to_nine(17) --> [seventeen].
ten_to_nine(18) --> [eighteen].
ten_to_nine(19) --> [nineteen].

multiple_of_ten(20) --> [twenty].
multiple_of_ten(30) --> [thirty].
multiple_of_ten(40) --> [forty].
multiple_of_ten(50) --> [fifty].
multiple_of_ten(60) --> [sixty].
multiple_of_ten(70) --> [seventy].
multiple_of_ten(80) --> [eighty].
multiple_of_ten(90) --> [ninety].