我有一个幂函数pow
,它会尝试计算B
的值E
。到目前为止,我处理案件 -
1.指数为0
2.指数非零
pow(B,0,1).
pow(B,E,Result):- E2 is E - 1,
pow(B,E2,Result2),
Result is B*Result2.
如何添加电源功能可以处理负指数的另一种情况?
答案 0 :(得分:4)
首先,应该考虑如何定义0 0 。从形式上讲,它是不确定。它可能是零或者可能是1.正如Wolfram的Mathworld在其article on powers及其article on zero中所说的那样:
0 0 (第零次幂为零)本身未定义。对于这个数量缺乏明确定义的含义来自相互矛盾的事实, a 0 总是1,所以0 0 应该相等1,但0 a 始终为0(对于 a > 0),所以0 a 应该等于0. 0 0 的定义选择通常被定义为不确定的,尽管定义0 0 = 1允许一些公式简单表达( Knuth 1992; Knuth 1997,p.57)。
所以你应该首先选择如何定义0 0 的特殊情况:它是0吗?是1吗?是不确定的?
我选择将其视为未定义。
话虽如此,你可以看一个正指数,如重复乘法所示(例如10 3 是10 * 10 * 10,或1000),你可以看一个负指数作为指示重复划分(例如,10 -3 是(((1/10)/ 10)/ 10),或0.001)。我的倾向,部分是因为我喜欢这种方法的对称性,部分是为了避免切割(因为切割通常是你没有正确定义解决方案的信号),将是这样的:
% -----------------------------
% The external/public predicate
% -----------------------------
pow( 0 , 0 , _ ) :- ! , fail .
pow( X , N , R ) :-
pow( X , N , 1 , R )
.
% -----------------------------------
% the tail-recursive worker predicate
% -----------------------------------
pow( _ , 0 , R , R ).
pow( X , N , T , R ) :-
N > 0 ,
T1 is T * X ,
N1 is N-1 ,
pow( X , N1 , T1 , R )
.
pow( _ , 0 , R , R ) :-
N < 0 ,
T1 is T / X ,
N1 is N+1 ,
pow( X , N1 , T1 , R )
.
正如其他人所指出的那样,另一种方法是将正指数定义为指示重复乘法,将负指数定义为指示正指数的倒数,因此10 3 为10 * 10 * 10或1,000,和10 -3 是1 /(10 3 ),或1 / 1,000或0.001。为了使用这个定义,我再次避免削减并做这样的事情:
% -----------------------------
% the external/public predicate
% -----------------------------
pow( 0 , 0 , _ ) :- % 0^0 is indeterminate. Is it 1? Is it 0? Could be either.
! ,
fail
.
pow( X , N , R ) :-
N > 0 ,
pow( X , N , 1 , R )
.
pow( X , N , R ) :-
N < 0 ,
N1 = - N ,
pow( X , N1 , 1 , R1 ) ,
R is 1 / R1
.
% -----------------------------------
% The tail-recursive worker predicate
% -----------------------------------
pow( _ , 0 , R , R ).
pow( X , N , T , R ) :-
N > 0 ,
T1 is T * X ,
N1 is N-1 ,
pow( X , N1 , T1 , R )
.
答案 1 :(得分:2)
首先,你的第二个子句是非尾递归的(你可以阅读主题here)。这意味着最终,在运行它时,你将耗尽调用堆栈内存。 一个好处是使用累加器使其尾递归。您可以按如下方式实现:
% we add an accumulator to poW/3, making it pow/4.
pow(B, E, Result) :- pow(B, E, 1, Result).
% when we hit 0, our accumulator holds B^E so we unify it with result.
pow(_, 0, Accu, Accu) :- !.
% at each step, we multiply our accumulator by B
pow(B, E, Accu, Result) :-
NewE is E - 1,
NewAccu is Accu * B,
pow(B, NewE, NewAccu, Result).
然后,您可以通过在其他句子之上添加此子句来简单地处理否定案例(它只是告诉序言负面权力是正面的反面):
pow(B, E, Result) :-
E < 0,
PositiveE is - E,
pow(B, PositiveE, 1, R),
!,
Result is 1 / R.
请注意,您可以直接使用代码执行此操作:
pow(B, E, Result) :-
E < 0,
PositiveE is - E,
pow(B, PositiveE, R),
!,
Result is 1 / R.
另外,我们现在引入了一个非常红的切口(如果需要,请参阅here了解红切的含义)。因此,最好通过这种修改变成绿色切割:
pow(B, E, Result) :-
E < 0,
PositiveE is - E,
pow(B, PositiveE, 1, R),
!,
Result is 1 / R.
% we add an accumulator to poW/3, making it pow/4.
pow(B, E, Result) :-
E >= 0, %************* HERE *****************
pow(B, E, 1, Result).
% when we hit 0, our accumulator holds B^E so we unify it with result.
pow(_, 0, Accu, Accu) :- !.
% at each step, we multiply our accumulator by B
pow(B, E, Accu, Result) :-
NewE is E - 1,
NewAccu is Accu * B,
pow(B, NewE, NewAccu, Result).
答案 2 :(得分:2)
别忘了a^(2b) = (a^b)^2
和x^2 = x*x
。可以使用累加器以非尾部方式从顶级&#34; UI&#34;中调用尾递归工作谓词。谓语。这样你就不必为负面的权力实现工作谓词,而是重用一个用于正面的权力,并改变它在顶级谓词中的结果(我看到这已被提出):
pow(B, E, R):- E<0 -> ... ; E=:=0 -> ... ; E>0 -> ... .