试图弄清楚如何编写一个递归谓词divide_by(X,D,I,R),它将正整数X和除数D作为输入,并将答案作为整数部分I和余数部分R返回,但是,我似乎无法理解 Prolog。我该怎么做呢?
3 回答
为此,有预定义的可评估函子。
(div)/2
并且(mod)/2
总是四舍五入。LIA-1、Knuth 等推荐。
(//)/2
并向(rem)/2
零舍入(实际上,它是定义的实现,但所有当前的实现都是这样做的)。current_prolog_flag(integer_rounding_function, F)
你可以通过which give in current implementations来询问这个问题toward_zero
。
这些对之间的差异仅在涉及负数时显示。这是一场人们更喜欢的宗教战争。ISO/IEC 10967:2012 与语言无关的算术 (vl. LIA-1) 仅提供向下舍入“由于易于错误使用” (C.5.1.2.2) 的向零,而 Fortran 和像 C 之类的追随者去向零调用它“代数”(6.5.5)。另请参阅: 使用截断朝向负无穷与朝向零的优点
我假设您的老师正在尝试教授递归。
除法是重复减法,就像乘法是重复加法一样,n'est-ce-pas ?
由于变量只能被赋值一次,一个常见的 prolog 习惯用法是有一个外部“公共”谓词,它调用一个使用累加器并执行实际工作的私有“工人”谓词。这导致我们得到这样的解决方案。
%
% divide M (the dividend) by N (the divisor),
%yielding the quotient (Q) and remainder (R).
integer_division( M , N , Q , R ) :-
M > 0 ,
N > 0 ,
div_rem( M , N , 0 , Q , R )
.
%
% internal worker predicate for integer division
% requires dividend and divisor both to be unsigned (positive).
%
div_rem( M , N , Q , Q , M ) :- % dividend M < divisor N? We're done.
M < N , % - unify the accumulator with the quotient
. % - and unify the divisor with the remainder
div_rem( M , N ,T , Q , R ) :- % dividend M >= divisor N?
M >= N , % - increment the accumulator T
T is T+1 , % - decrement the divisor by N
M1 is M-N , % - recurse down
div_rem( M1 , N , T1 , Q , R ) % - That's all.
. % Easy!
从这里可以很容易地修改外部公共谓词来解释有符号的操作数,记住
+/+
产量+
+/-
产量-
-/+
产量-
-/-
产量+
并M/N
求得商Q
和余数R
,性质
M = N * Q + R
成立。这应该会通知您商和余数所需的符号。别忘了负数的加法和减法一样:M + -N
等价于M - N
. 这将使您获得数学上正确的结果(可能与通过计算机的整数除法指令获得的结果不同)。应该注意,要使上述属性为真,商的符号和余数的符号可能不同。
如果您必须将算术运算限制为加法、减法,那么您可以使用递归,如下所示:
divit(Dividend, Divisor, Q, R) :-
Dividend > 0, % Only allow positive dividend
Divisor \== 0, % Don't allow 0 divisor
% Use an accumulator (starts at 0) for the quotient, counting subtractions
% of the divisor from the dividend
divit(Dividend, Divisor, 0, Q, R).
% If Dividend < Divisor, then
% quotient accumulator is quotient and dividend is remainder, done!
divit(Dividend, Divisor, Qacc, Qacc, Dividend) :-
Dividend < Divisor.
% If Dividend >= Divisor, then
% subtract the divisor from the dividend
% increment the quotient accumulator
% recurs on updated dividend and quotient accumulator until dividend < divisor
divit(Dividend , Divisor, Qacc, Q, R) :-
Dividend >= Divisor,
D1 is Dividend - Divisor,
Qacc1 is Qacc + 1,
divit(D1, Divisor, Qacc1, Q, R).