Программа Prolog не будет вычислять переменный ответ?

Это должно быть легко исправить, но я не могу справиться с этим, и это становится расстраивающим. Я написал программу, которая вычисляет или проверяет, связаны ли два списка, поскольку все элементы второго списка увеличиваются на единицу относительно элементов первого списка. Это работает, когда даны два списка, но не когда нужно вычислить список.

Код выглядит следующим образом:

inc([], []).
inc([X|XS],[Y|YS]) :- 
  Y =:= X+1,
  inc(XS,YS).
ERROR: =:=/2: Arguments are not sufficiently instantiated

Любая помощь будет принята с благодарностью!


person Kevin Cruz    schedule 29.05.2013    source источник
comment
@CapelliC, который решает только одно направление.   -  person Daniel Lyons    schedule 29.05.2013
comment
@DanielLyons: я знаю, просто предложил (ИМХО) более простое исправление. Кстати, succ/2 примерно в два раза медленнее, чем арифметика.   -  person CapelliC    schedule 29.05.2013
comment
@CapelliC всегда есть компромиссы. :)   -  person Daniel Lyons    schedule 29.05.2013


Ответы (2)


inc([],[]).
inc([X|XS],[Y|YS]) :-
  nonvar(X),
  Z is X + 1,
  Y = Z,
  inc(XS,YS), !.
inc([X|XS],[Y|YS]) :-
  nonvar(Y),
  Z is Y - 1,
  X = Z,
  inc(XS,YS), !.

Здесь нам нужно получить реальное вычисление для сложения, а затем попытаться создать экземпляр с помощью =. Предикат пришлось разделить, чтобы иметь дело со случаем, когда X не был создан, и когда Y не был создан. ! в конце каждого из них не позволяет ему пытаться найти больше решений после того, как он нашел одно через один из двух похожих путей.

person lurker    schedule 29.05.2013
comment
Большое спасибо! Я также экспериментировал с назначением отдельной переменной для сравнения, но по какой-то причине она не работала для меня так, как я хотел, из-за моего компаратора =:=. Означает ли это, что невозможно найти единственный предикат, который мог бы вычислять как левостороннее, так и правостороннее выражение? - person Kevin Cruz; 29.05.2013
comment
Это не работает, если оба аргумента являются переменными (например: ?- inc(X, Y).). Рассмотрите возможность использования ограничений CLP(FD) для более общего решения. - person mat; 29.05.2013
comment
@Дэниел. Спасибо за ваши комментарии к моему плохому объяснению. Удалил, чтобы больше не путать. Я предположил, не задумываясь, за что прошу прощения. Тем не менее, я думаю, что было бы несправедливо изображать это как то, что у меня есть довольно странные идеи о Прологе в этих ограниченных обстоятельствах. Кроме того, для оператора (и для меня) было бы больше пользы, если бы, основываясь на вашем очевидном большем опыте, дать исправленное объяснение. - person lurker; 29.05.2013
comment
@mbrach Я думаю, что исправленное объяснение содержится в моем ответе ниже. Прошу прощения за формулировку в моем комментарии; Я удалил его. - person Daniel Lyons; 29.05.2013

Ваша проблема, по сути, заключается в том, что =:=/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
comment
Ваш код не работает, например, для самого общего запроса: ?- inc(X, Y).. Это происходит, если вы используете CLP(FD). - person mat; 29.05.2013
comment
Я попробовал решения на основе maplist и succ на inc(X,[0,3])., и это не удалось. Однако решение, которое я предложил без использования succ или maplist, сработало и дало X = [-1,2]. - person lurker; 29.05.2013
comment
@Дэниел, круто! Мне это нравится. Спасибо. :) - person lurker; 29.05.2013
comment
Я только что попробовал inc([1,2],[2,3]). на версии maplist/isucc, и он вернулся no. Тем не менее, он хорошо справляется со случаями inc(X,[...]). и inc([...],Y).. - person lurker; 29.05.2013
comment
@mbrach Это потому, что ни 1, ни 2 не проходят var/1. Посмотрим, сможешь ли ты это исправить. - person Daniel Lyons; 29.05.2013
comment
Просто удалите var(Y), из второго isucc. Итак, получается: isucc(X, Y) :- Y is X+1. Я думаю, что об этом позаботится. Ницца. - person lurker; 30.05.2013