Prolog - 两个递归规则后的打印结果|平方和

时间:2017-02-04 19:52:08

标签: recursion prolog

我是prolog的新手,我觉得有一个我无法理解的概念,这使我无法理解prolog中的递归概念。我试图返回S,它是每个数字的平方和,作为用户在查询中输入的整数列表。 E.g用户输入12345,我必须返回S = (1^2)+(2^2)+(3^2)+(4^2)+(5^2) = 55

在我的程序中,我理解为什么S的每个计算段都被多次打印,因为它是递归规则的一部分。但是,我不明白我将如何打印S作为最终结果。我想我可以将变量=设置为第二个规则中sos的结果,并将其作为intToList的参数添加,但似乎无法想出这一个。编译器警告S是intToList规则中的单例变量。

sos([],0).
sos([H|T],S) :- 
  sos(T, S1), 
  S is (S1 + (H * H)),
  write('S is: '),write(S),nl.

intToList(0,[]).
intToList(N,[H|T]) :- 
  N1 is floor(N/10), 
  H is N mod 10, 
  intToList(N1,T),
  sos([H|T],S).

2 个答案:

答案 0 :(得分:1)

试试这个:

sos([],Accumulator,Accumulator).

sos([H|T],Accumulator,Result_out) :-
    Square is H * H,
    Accumulator1 is Accumulator + Square,
    sos(T,Accumulator1,Result_out).

int_to_list(N,R) :-
    atom_chars(N,Digit_Chars),
    int_to_list1(Digit_Chars,Digits),
    sos(Digits,0,R).

int_to_list1([],[]).

int_to_list1([Digit_Char|Digit_Chars],[Digit|Digits]) :-
    atom_number(Digit_Char,Digit),
    int_to_list1(Digit_Chars,Digits).

对于int_to_list,我使用内置的atom_chars,例如

?- atom_chars(12345,R).
R = ['1', '2', '3', '4', '5'].

然后使用典型循环使用atom_number将每个字符转换为数字,例如

?- atom_number('2',R).
R = 2.

对于sos我使用累加器来累积答案,然后一旦列表为空,将累加器中的值移动到结果

sos([],Accumulator,Accumulator).

请注意,累加器有不同的变量,例如

Accumulator1 is Accumulator + Square,
sos(T,Accumulator1,Result_out).

这是因为在Prolog中变量是不可变的,因此无法继续为同一变量分配新值。

以下是一些示例运行

?- int_to_list(1234,R).
R = 30.

?- int_to_list(12345,R).
R = 55.

?- int_to_list(123456,R).
R = 91.

如果您有任何问题,请在本答复的评论中提问。

答案 1 :(得分:1)

原始代码存在的问题是,您正试图在sos/2的递归条款中处理对intToList/2的调用。将其分解(并将intToList/2重命名为更有意义的内容):

sosDigits(Number, SoS) :-
    number_digits(Number, Digits),
    sos(Digits, SoS).

这是您原来的sos/2没有write,这似乎工作正常:

sos([], 0).
sos([H|T], S) :- 
  sos(T, S1), 
  S is (S1 + (H * H)).

或者更好的是,使用累加器进行尾递归:

sos(Numbers, SoS) :-
    sos(Numbers, 0, SoS).
sos([], SoS, SoS).
sos([X|Xs], A, SoS) :-
    A1 is A + X*X,
    sos(Xs, A1, SoS).

您还可以使用sos/2maplist/3

来实施sumlist/2
square(X, S) :- S is X * X.
sos(Numbers, SoS) :- maplist(square, Numbers, Squares), sumlist(Squares, SoS).

您的intToList/2需要使用累加器进行重构,以维持正确的数字顺序并摆脱对sos/2的调用。如上所述重命名:

number_digits(Number, Digits) :-
    number_digits(Number, [], Digits).

number_digits(Number, DigitsSoFar, [Number | DigitsSoFar]) :-
    Number < 10.
number_digits(Number, DigitsSoFar, Digits) :-
    Number >= 10,
    NumberPrefix is Number div 10,
    ThisDigit is Number mod 10,
    number_digits(NumberPrefix, [ThisDigit | DigitsSoFar], Digits).

上述number_digits/2也正确处理0,因此number_digits(0, Digits)会产生Digit = [0]而不是Digits = []

您可以使用number_digits/3构造重写-> ;的上述实现:

number_digits(Number, DigitsSoFar, Digits) :-
    (   Number < 10
    ->  Digits = [Number | DigitsSoFar]
    ;   NumberPrefix is Number div 10,
        ThisDigit is Number mod 10,
        number_digits(NumberPrefix, [ThisDigit | DigitsSoFar], Digits)
    ).

然后它不会留下选择点。