2017-10-12 96 views
1

我需要在SWI-prolog中乘以加法的简单功能。例如m(X,Y,Z),例如X = 5,Z = 3 < ==> 5 * 3。 Y是结果:Y = 5,Y = 10,Y = 15 [停止]。我在想这样的事情:Prolog - 通过加法相乘

m(X,Y,Z):- Z>0, /*when Z reaches 0 you stop */ I=X+X, W=Z-1, m(I,Y,W). 

但它总是返回“假”,不知道为什么。

+2

'瘾“真的吗? –

+3

@GuyCoder:当然是对Prolog的沉迷:) –

+3

宁可开始使用[tag:后继算术]! – false

回答

2

让我们从思考谓词应该描述什么开始:它是三个数字之间的关系,其中第三个数字是前两个数字的乘积。既然你想通过将第二个参数减少到零来描述乘法,而把第一个加起来相应地多次,我们正在谈论自然数。所以一个很好的描述性名字是nat_nat_prod/3。接下来考虑可能的情况:

  1. 第二个参数可以为零。然后,由于X * 0 = 0,产品也必须为零。所以这是基本情况。

  2. 否则第二个参数大于零。然后你想把它减1并计算第一个参数和这个新数字的乘积。由于谓词可以用它来描述,所以这是一个递归的目标。随后,您将第一个参数添加到递归所描述的中间产品中。

这可以在序言写像这样:

nat_nat_prod(_X,0,0).   % case 1) 
nat_nat_prod(X,Y1,P1) :-  % case 2) 
    Y1 > 0, 
    Y0 is Y1-1, 
    nat_nat_prod(X,Y0,P0), 
    P1 is P0+X. 

现在让我们尝试一些疑问:

?- nat_nat_prod(5,3,P). 
P = 15 ; 
false. 

?- nat_nat_prod(5,4,P). 
P = 20 ; 
false. 

?- nat_nat_prod(5,0,P). 
P = 0 ; 
false. 

?- nat_nat_prod(1,0,P). 
P = 0 ; 
false. 

?- nat_nat_prod(1,1,P). 
P = 1 ; 
false. 

然而,谓语玩耍的时候,你会发现,前两个参数必须被实例化,否则你会得到一个错误:

?- nat_nat_prod(1,Y,3). 
ERROR: >/2: Arguments are not sufficiently instantiated 
?- nat_nat_prod(X,1,3). 
ERROR: is/2: Arguments are not sufficiently instantiated 

发生这种情况是由于使用>/2和/ 2。你可以通过使用CLP(FD)来解决这个问题,但我认为那不是重点。定义乘法的这种方式显然是非常低效相比,使用标准的算术函数*/2,如:

?- time(nat_nat_prod(2,1000000,P)). 
% 3,000,000 inferences, 33.695 CPU in 33.708 seconds (100% CPU, 89035 Lips) 
P = 2000000 ; 
% 3 inferences, 0.031 CPU in 0.031 seconds (100% CPU, 97 Lips) 
false. 

?- time(P is 2*1000000). 
% 1 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 82325 Lips) 
P = 2000000. 

正如已经@false中更常见的向人们介绍successor arithmetics第一,然后评论暗示以这种方式定义s(X)符号中两个数字的加/乘。由于不能使用s(X)数字的标准算术函数,因此也不会遇到关联的实例化错误。

+1

通过使用累加器,代码可以是尾递归的,这会给出100倍的加速!与以前一样使用(ful | less),但速度更快;) – repeat

+1

@repeat:你对此绝对正确。然而,我的想法是谓词的非关系行为由于实例化错误而成为实际的缺点。我只用速度参数来指出为什么我不会抛弃CLP(FD)来解决这个问题。一个100倍的加速并不会真的改变,它只会将我的axample查询从1000000转移到100000000 ;-) – tas

+1

@repeat:我实际上也编写了一个尾递归版本,所以我可以通过因子100来确认加速,但是,因为它由一个带有3个参数的调用谓词和一个带有额外参数的实际谓词组成,我选择发布较短的代码。你认为我应该发布尾递归版本吗? – tas