Я изменил свои правила таким образом
solve(TargetX, TargetY, _, _, NewSquare, [NewSquare]) :-
connects(NewSquare, square(TargetX, TargetY)).
solve(TargetX, TargetY, SourceX, SourceY, NewSquare, [E | Tail]) :-
connects(NewSquare, E),
solve(TargetX, TargetY, SourceX, SourceY, E, Tail).
solveFirst(TargetX, TargetY, SourceX, SourceY, [NewSquare | T]):-
connects(square(SourceX, SourceY), NewSquare),
solve(TargetX, TargetY, SourceX, SourceY, NewSquare, T).
и со следующими фактами
square(1, 1). square(1, 2). square(1, 3).
square(2, 1). square(2, 2). square(2, 3).
square(3, 1). square(3, 2). square(3, 3).
connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).
вызова solveFirst(3, 3, 1, 1, L)
, я (L
) следующие пути
[square(1,2),square(2,2),square(3,2),square(3,2)]
[square(2,1),square(2,2),square(3,2),square(3,2)]
Но эта работа из-за отсутствия петель. Если добавить следующее соединение
connects(square(2, 2), square(1, 2)).
так что вы можете цикл ((1,2) -> (2,2) -> (1,2) -> (2,2) ...) и из solveFirst(3, 3, 1, 1, L)
Я получаю переполнение стека.
Чтобы избежать этой проблемы, вы можете вспомнить посещенные квадраты и не использовать их снова.
Я написал следующий пример, но считаю, что
(1) Я перешел начало и цель (запуск первой, второй целевой)
(2) Я добавил начало и цель в результирующий путь
(3) Я использую gprolog, поэтому у меня нет not/1
; Я использовал \+ member....
вместо
getPath(Tx, Ty, Tx, Ty, _, [square(Tx, Ty)]).
getPath(Sx, Sy, Tx, Ty, Visited, [square(Sx, Sy) | Path]) :-
connects(square(Sx, Sy), square(Nx, Ny)),
\+ member(square(Nx, Ny), Visited), % or not(member(square(Nx, Ny), Visited)
getPath(Nx, Ny, Tx, Ty, [square(Nx, Ny) | Visited], Path).
getPath(Sx, Sy, Tx, Ty, Path) :-
getPath(Sx, Sy, Tx, Ty, [square(Sx, Sy)], Path).
Используя следующие факты
square(1, 1). square(1, 2). square(1, 3).
square(2, 1). square(2, 2). square(2, 3).
square(3, 1). square(3, 2). square(3, 3).
connects(square(1, 1), square(1, 2)).
connects(square(1, 1), square(2, 1)).
connects(square(1, 2), square(1, 3)).
connects(square(1, 2), square(2, 2)).
connects(square(2, 2), square(1, 2)).
connects(square(2, 1), square(2, 2)).
connects(square(2, 2), square(3, 2)).
connects(square(3, 2), square(3, 3)).
из getPath(1, 1, 3, 3, L)
я получаю следующие пути
[square(1,1),square(1,2),square(2,2),square(3,2),square(3,3)]
[square(1,1),square(2,1),square(2,2),square(3,2),square(3,3)]
--- EDIT ---
Как было предложено Mat in комментарий (спасибо!), вместо \+ member(square(Nx, Ny), Visited)
(или not(member(square(Nx, Ny), Visited)
) вы могли бы написать (если пролог поддержка среды maplist/2
)
maplist(dif(square(Nx,Ny)), Visited)
навязать, что square(Nx, Ny)
не в Visited
списке.
Это решение является более общим, потому что (если я правильно понимаю) унификация работает в обоих направлениях.
Хорошее решение, +1! Только одно небольшое предложение: рассмотрите использование 'dif/2', чтобы выразить, что два члена отличаются. Это делает ваш предикат более общим. – mat
@mat - спасибо; Я даже удивлен, что кто-то понимает, что я пишу; но ... где я должен использовать 'dif/2'? – max66
'maplist (dif (square (Nx, Ny)), Посещенные)' states: 'square (Nx, Ny)' is * different * от каждого из элементов в 'Посещенные'. Критически это работает правильно во всех * направлениях, также если 'Посещенные' не создаются. Поэтому 'dif/2' делает ваш код более универсальным и общим. – mat