Как получить сумму заданных чисел в prolog?
Я новичок в prolog, и я делаю некоторые упражнения для практики. Поэтому я пытаюсь получить сумму данных чисел в списке. Я пытаюсь использовать это:
my_last(X, [X]).
my_last(X, [_|L]) :- my_last(X, L).
(от здесь)
как мой гид. Итак, это мой код для получения суммы:
listsum(X, []).
listsum(X, [H|L]):-
X is H + listsum(X, L).
когда я скомпилировать его, он говорит
практика.pl: 3: оценочный
listsum(_G139,_G140)
не существует
практика.pl: 2: одноэлементные переменные:[X]
тогда, когда я попытаюсь listsum(0, [1,2,3]).
возвращает false
.
Я все еще не очень понимаю пролог, а также список и рекурсию в прологе.
2 ответов
арифметика
как вы уже обнаружили, арифметика может быть обработана в прологе с помощью (is)/2
оператора. Это потому, что в прологе все является только символическим исчислением: вещи не имеют значения по умолчанию, поэтому объединение (=)/2
не знаю (+)/2
относится к добавлению, например.
Итак, ваша проблема в том, что вы используете обычный предикат внутри (is)/2
(вот ваш рекурсивный вызов). С (is)/2
выполняет только арифметические, он не оценивает вызов предиката. Он даже не распознает его, так как это не арифметическая функция.
исправление здесь будет влиять на результат рекурсивного вызова переменной, а затем использовать его в (is)/2
звоните:
listsum(X,[]).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
базовый вариант корректность
но если вы протестируете этот код, вы не получите желаемого результата. Причина в том, что у вас есть другая проблема, в своем базовом варианте. Сумма пустого списка не является "ничем", как вы заявил пишу
listsum(X,[]).
(X
является свободной переменной, следовательно, может быть что угодно).
0
:
listsum(0, []).
результирующий код:
listsum(0, []).
listsum(Result, [Head|Tail]) :-
listsum(SumOfTail, Tail),
Result is Head + SumOfTail.
порядок аргументов
теперь, как sidenote, в Prolog соглашение заключается в том, что выходные переменные должны быть помещены в конец предиката, а входные переменные должны быть помещены в начало предиката, поэтому, чтобы вести себя так, как мы хотели, мы могли бы рефакторировать как следует:
listsum([], 0).
listsum([Head|Tail], Result) :-
listsum(Tail, SumOfTail),
Result is Head + SumOfTail.
Оптимизация Хвостового Вызова
теперь мы все еще можем улучшить этот предикат с помощью более продвинутых методов. Например, мы могли бы ввести хвостовые вызовы, чтобы можно было выполнить оптимизацию хвостовых вызовов (googlable), благодаря идиоме декларативного программирования, называемой аккумулятором:
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator is Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
идея заключается в том, чтобы обновлять промежуточный результат на каждом шаге рекурсии (добавляя значение текущей главы списка к it), а затем просто укажите, что когда список пуст, это промежуточное значение является конечным значением.
получение более общих программ
как вы могли заметить, в прологе, довольно часто предикаты могут быть использованы несколькими способами. Например, length/2
можно использовать для обнаружения длины списка:
?- length([1, 2, 3], Length).
Length = 3.
или построить список скелетов со свободными переменными нужной длины:
?- length(List, 3).
List = [_G519, _G522, _G525].
здесь, однако, вы могли бы отметить, что вы не можете спросить Prolog каковы списки, которые имеют сумму, которая является 6
:
?- listsum(L, 6).
ERROR: is/2: Arguments are not sufficiently instantiated
это потому, что, чтобы" вернуться назад", Пролог должен был бы решить уравнение, когда приходит вызов (is)/2
оператора. И хотя ваш прост (только дополнения), арифметика не разрешима таким образом в общем случае.
чтобы преодолеть эту проблему, можно использовать Программирование ограничений. Очень хорошая библиотека доступна для SWI, clpfd.
синтаксис здесь быть:
:- use_module(library(clpfd)).
listsum(List, Sum) :-
listsum(List, 0, Sum).
listsum([], Accumulator, Accumulator).
listsum([Head|Tail], Accumulator, Result) :-
NewAccumulator #= Accumulator + Head,
listsum(Tail, NewAccumulator, Result).
теперь мы можем использовать наш предикат другим способом, который мы хотели бы использовать:
?- listsum(L, 6).
L = [6] ;
L = [_G1598, _G1601],
_G1598+_G1601#=6 ;
L = [_G1712, _G1715, _G1718],
_G1712+_G1715#=_G1728,
_G1728+_G1718#=6 . % Here I interrupted the answer but it would not terminate.
мы могли бы даже попросить все решения проблемы:
?- listsum(L, X).
L = [],
X = 0 ;
L = [X],
X in inf..sup ;
L = [_G2649, _G2652],
_G2649+_G2652#=X . % Here I interrupted the answer but it would not terminate
я только что упомянул это, чтобы вы поняли, что довольно часто используют (is)/2
следует избегать и использовать Программирование ограничений следует предпочесть, чтобы получить наиболее общие программы.