【问题标题】:Prolog - Printing Result After Two Recursive Rules | Sum of SquaresProlog - 两个递归规则后的打印结果平方和
【发布时间】:2017-02-04 19:52:07
【问题描述】:

我是 prolog 的新手,我觉得有一个我无法理解的概念,这使我无法掌握 prolog 中递归的概念。我试图返回S,它是每个数字的平方和,作为用户在查询中输入的整数的列表。例如用户输入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).

【问题讨论】:

    标签: recursion prolog


    【解决方案1】:

    您的原始代码的问题是您试图在intToList/2 的递归子句中处理对sos/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).
    

    您还可以使用maplist/3sumlist/2 实现sos/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 = []

    您可以使用-&gt; ; 构造重写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)
        ).
    

    那么它就不会留下选择点了。

    【讨论】:

    • 感谢您的详尽解释,非常有帮助。
    【解决方案2】:

    试试这个:

    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.
    

    如果您有任何问题,请在此答案下的 cmets 中提问。

    【讨论】:

    • 您可以选择使用maplist 将您的数字字符列表转换为数字列表:maplist(atom_number, Digit_Chars, Digits),这样就不需要int_to_list1/2。对于每个元素的列表处理/映射来说,这是一个很好的标准谓词。
    • 谢谢你很有帮助的解释。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-10
    • 1970-01-01
    相关资源
    最近更新 更多