Сумма элементов в списке в Prolog
list_sum([], 0).
list_sum([Head | Tail], TotalSum) :-
list_sum(Tail, Sum1),
Total = Head + Sum1.
этот код возвращает true
. Если я заменю Total = Head + Sum1
С Total is Head + Sum1
, затем он вернет значение. Но что я должен заменить, чтобы получить такой результат:
?- list_sum([1,2,0,3], Sum).
Sum = 1+2+0+3 ; % not to return value 6!!!
5 ответов
обратите внимание,что во втором предложении вашей процедуры TotalSum никогда не создается. Вы должны были получить предупреждение от переводчика при консультации с вашим кодом.
вот мое предложение:
list_sum([Item], Item).
list_sum([Item1,Item2 | Tail], Total) :-
list_sum([Item1+Item2|Tail], Total).
первое предложение имеет дело с базовым случаем, когда в списке остается только один элемент - ваш результат.
второе предложение касается шага рекурсии. Он захватывает первые два элемента списка и выполняет рекурсивный вызов replacing эти два элемента с новым термином Item1+Item2.
ответ прост:
sum_list([], 0).
sum_list([H|T], Sum) :-
sum_list(T, Rest),
Sum is H + Rest.
этот код работает только в одном направлении - это значит - она не позволяет создавать списки с этой конкретной суммы. Но поскольку набор таких списков бесконечен, это все равно было бы непрактично.
В Пролог (+)/2
- это двоичный infix оператора. Это позволяет нам писать A+B
вместо +(A,B)
.
?- current_op(_,yfx,+). % left-associative binary infix operator true.
(+)/2
партнеры слева, так что 1+2+3
это сокращение от (1+2)+3
.
(.)/2
присоединяется к право, так что [1,2,3]
сокращенно .(1,.(2,.(3,[])))
.
чтобы получить parenthesization право, мы используем вспомогательный предикат с собой дополнительный "аккумулятор" аргумент:
list_sum([X|Xs],S) :-
list_sum0_sum(Xs,X,S).
list_sum0_sum([], S ,S).
list_sum0_sum([X|Xs],S0,S) :-
list_sum0_sum(Xs,S0+X,S).
образец запрос:
?- list_sum([1,2,0,3],S).
S = 1+2+0+3.
Если вы хотите преобразовать список чисел в аддитивное выражение, из
[1,2,3]
to
1 + 2 + 3
вы можете сделать что-то вроде этого, используя что-то вроде список разница:
list_to_additive_expr( [] , 0 ).
list_to_additive_expr( [X|Xs] , X + RHS ) :-
sum_of( Xs , RHS ).
кроме того, вы можете использовать аккумулятор:
list_to_additive_expr( Xs , Expr ) :-
list_to_additive_expr( Xs , 0 , Expr )
.
list_to_additive_expr( [] , Expr , Expr ) .
list_to_additive_expr( [X|Xs] , RHS , Expr ) :-
sum_of( Xs , X + RHS , Expr )
.
Я считаю, что вы найдете первый стиль не правильно хвост рекурсивной и поэтому не будет оптимизирован в цикл via оптимизация хвостовой рекурсии (TRO) - и поэтому, если список достаточно длинный, получит переполнение стека. Второй подход должен быть применен TRO и должен работать для списков любой длины.
что такое ТРО, спросите вы? Вот!--24-->Википедия с ответом для вас:
в информатике вызов хвоста-это вызов подпрограммы, который происходит внутри другого процедура и которая производит возвращаемое значение, которое после этого немедленно возвращенный вызывающая процедура. Затем говорят, что сайт вызова находится в хвостовом положении, т. е. в конце вызывающая процедура. Если подпрограмма выполняет хвостовой вызов для себя, она вызывается хвост-рекурсивный. Это частный случай рекурсии.
хвостовые вызовы значительны, потому что они могут быть реализованы без добавления нового стека кадр в стек вызовов. Большая часть рамок текущей процедуры не требуется больше, и его можно заменить рамкой хвоста вызов, измененный соответствующим образом (аналогично overlay для процессов, но для вызовов функций). Затем программа может перейти к называемая подпрограмма. Создание такого кода вместо стандартной последовательности вызовов называется устранение хвостового вызова или оптимизация хвостового вызова.
программа
list_sum([],0).
list_sum([Head|Tail], TotalSum):-
list_sum(Tail, Sum1),
TotalSum is Head+Sum1.
Теперь, если запрос
?- list_sum([1,2,3,4], Sum).
ответ
Sum = 10