2017-02-04 6 views
0

Я совершенно новый для пролога, и я чувствую, что есть концепция, которую я не понимаю, что мешает мне понять концепцию рекурсии в прологе. Я пытаюсь вернуть S, что является суммой квадрата каждой цифры, взятой в виде списка из целого числа, введенного пользователем в запросе. Например, пользователь вводит 12345, я должен вернуть S = (1^2)+(2^2)+(3^2)+(4^2)+(5^2) = 55.Prolog - Результат печати после двух рекурсивных правил | Sum of Squares

В моей программе ниже я понимаю, почему каждый сегмент вычисления 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). 

ответ

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). 

это потому, что в Прологе переменные неизменны, поэтому никто не может держать присваиванием новых значений одной и той же переменной.

Вот несколько примеров работает

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

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

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

Если у вас есть какие-либо вопросы, просто спросите в комментариях под этим ответом.

+0

Возможно, вы можете использовать 'maplist' для преобразования списка цифр в список цифр:' maplist (atom_number, Digit_Chars, Digits) ', что устраняет необходимость в' int_to_list1/2'. Это хороший стандартный предикат для обработки/сопоставления списков для каждого элемента. – lurker

+0

Спасибо, очень полезно объяснение. – hailnick

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/2 с помощью maplist/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) 
    ). 

Тогда он не оставит точку выбора.

+0

Спасибо за подробное объяснение, это было очень полезно. – hailnick