Ваша проблема, по сути, заключается в том, что =:=/2
предназначен для тестирования, а не для установления привязок, хотя is/2
по-прежнему не делает то, что вы хотите. Например, хотя 2 is 1 + 1
истинно, 2 is X+1
не приведет к тому, что X
будет привязано к 1, потому что is/2
предполагает, что слева будет только одна переменная или значение, а справа одно выражение, и оно не ведет себя "относительно", как is/2
. остальная часть Пролога. Если вам нужна арифметика, которая ведет себя таким образом, вы должны проверить clpfd
; взгляд на сложность, которую он добавляет, является хорошим объяснением того, почему вещи такие, какие они есть.
К счастью, вам не нужна вся арифметика, чтобы решить вашу задачу. Встроенная функция succ/2
сделает именно то, что вам нужно, и в качестве бонуса вы получите однострочное решение:
inc(X, Y) :- maplist(succ, X, Y).
В использовании:
?- inc([1,2,3], [2,3,4]).
true.
?- inc([1,2,3], X).
X = [2, 3, 4].
?- inc(X, [1,2,3]).
X = [0, 1, 2].
Ваш код также отлично работает, если вы используете succ/2
вместо =:=/2
:
inc([], []).
inc([X|XS],[Y|YS]) :-
succ(X, Y),
inc(XS,YS).
Это должно быть "легкое исправление", о котором вы подозревали. :)
Я не уверен, что @mbrach имеет в виду «слишком много переменных» для одного предиката. Я подозреваю, что это неправильное понимание Пролога с их стороны, возможно, пережиток других языков, где функция может возвращать одно значение или что-то в этом роде. Здесь нет технических ограничений; предикаты могут принимать столько основных или неосновных аргументов и связывать столько из них, сколько вы хотите; ограничивающим фактором является ваше творчество.
Точно так же я не думаю, что «асимметрия» здесь имеет смысл. Вполне нормально определять предикаты, которые имеют только один шаблон создания экземпляров, но также нормально и предпочтительно создавать предикаты, которые гибки в отношении создания экземпляров — вы не можете заранее знать, какое использование может понадобиться в будущем. Вы можете подумать про себя, что шаблон инстанцирования, уничтожающий информацию, может помешать обратному шаблону инстанцирования, но на практике часто вместо этого вы можете превратить его в генератор.
Чтобы привести банальный пример, имя append/3
, кажется, подразумевает этот шаблон:
?- append([1,2], [3,4], X).
X = [1,2,3,4]
Это совершенно хорошее использование, но так оно и есть:
?- append(X, Y, [1,2,3,4]).
Это недетерминированный шаблон создания экземпляров, который дает пять решений:
X = [], Y = [1,2,3,4]
X = [1], Y = [2,3,4]
X = [1,2], Y = [3,4]
X = [1,2,3], Y = [4]
X = [1,2,3,4], Y = []
Это кажется противоречащим некоторым идеям @mbrach, но в обычном определении append/3
нет явного тестирования для наземного/неназемного, потому что это не обязательно, и аналогично со вторым шаблоном вызова вы получаете два «возвращаемых значения» с одного входа. источник SWI:
append([], L, L).
append([H|T], L, [H|R]) :-
append(T, L, R).
Изменить: отрицательные числа. Я забыл, что succ/2
определяется только для положительных целых чисел. Мы можем применить метод @mbrach и получить аккуратное решение с желаемыми свойствами:
isucc(X, Y) :- var(X), X is Y-1.
isucc(X, Y) :- Y is X+1.
inc(X, Y) :- maplist(isucc, X, Y).
В действии:
?- inc(X, [-1,2]).
X = [-2, 1] ;
false.
Редактировать: используя clp(fd) (через @mat):
fdsucc(X,Y) :- Y #= X + 1.
inc(X, Y) :- maplist(fdsucc, X, Y).
Это генерирует даже для самого общего запроса:
?- inc(X, Y).
X = Y, Y = [] ;
X = [_G467],
Y = [_G476],
_G467+1#=_G476 ;
X = [_G610, _G613],
Y = [_G622, _G625],
_G610+1#=_G622,
_G613+1#=_G625 ;
X = [_G753, _G756, _G759],
Y = [_G768, _G771, _G774],
_G753+1#=_G768,
_G756+1#=_G771,
_G759+1#=_G774
...
Полезность этого сомнительна, но, по-видимому, поскольку вы используете clp(fd), вы в конечном итоге наложите другие ограничения и получите что-то полезное.
person
Daniel Lyons
schedule
29.05.2013