Подтвердить что ты не робот

Определение пути/тропы/прогулки

Многие предикаты определяют какой-то ациклический путь, построенный из ребер, определенных через двоичное отношение, совершенно аналогично определяющему транзитивное закрытие. Таким образом, генерируется общее определение.

Обратите внимание, что понятия, определенные в теории графов, не всегда соответствуют ожидаемому. В частности, нас не интересуют имена краев.

Хуже того, теория графов немного изменилась, введя понятие walk, отметив

Традиционно путь относится к тому, что сейчас обычно называют открытым ходом. В настоящее время, когда заявлено без какой-либо квалификации, путь обычно понимается простым, что означает, что никакие вершины (и, следовательно, никакие ребра) не повторяются. (Термин цепочка также используется для обозначения ходьбы, в котором все вершины и ребра различны.)

Итак, мой вопрос: как назвать и определить эту функциональность?

То, что я сделал до сих пор, это определить:

path(Rel_2, Path, X0,X)

Первый аргумент должен быть продолжением отношения. Затем приходит либо Path, либо пара вершин.

Пример использования

n(a, b).
n(b, c).
n(b, a).

?- path(n,Xs, a,X).
Xs = [a], X = a ;
Xs = [a, b], X = b ;
Xs = [a, b, c], X = c ;
false.

Реализация

:- meta_predicate path(2,?,?,?).

:- meta_predicate path(2,?,?,?,+).

path(R_2, [X0|Ys], X0,X) :-
   path(R_2, Ys, X0,X, [X0]).

path(_R_2, [], X,X, _).
path(R_2, [X1|Ys], X0,X, Xs) :-
   call(R_2, X0,X1),
   non_member(X1, Xs),
   path(R_2, Ys, X1,X, [X1|Xs]).

non_member(_E, []).
non_member(E, [X|Xs]) :-
   dif(E,X),
   non_member(E, Xs).
4b9b3361

Ответ 1

Я хочу сосредоточиться на названии предиката.

  • В отличие от maplist/2, порядок аргументов здесь не имеет первостепенного значения.

  • Имя предиката должно четко указывать значение соответствующих аргументов.

Пока мне нравится path_from_to_edges лучше, но у него есть свои плюсы и минусы.

path_from_to_edges(Path,From,To,Edges_2) :-
    path(Edges_2,Path,From,To).

Пусть выделите его:

  • pro: path - существительное, оно не может быть неправильно прочитано глаголом. Для меня подразумевается список вершин.

  • pro: from обозначает вершину, а также to.

  • con: edges несколько расплывчато, но использование lambdas здесь является самым универсальным выбором.

  • con: Согласно Wikipedia, путь - это след, в котором все вершины (за исключением, возможно, первого и последнего ) различны. Поэтому в описании должно быть разъяснено.


Использование lambdas для списков соседних вершин Ess:

?- Ess  = [a-[b],b-[c,a]], 
   From = a,
   path_from_to_edges(Path,From,To,\X^Y^(member(X-X_neibs,Ess),member(Y,X_neibs))).
Ess = [a-[b],b-[c,a]], From = a, To = a, Path = [a]     ;
Ess = [a-[b],b-[c,a]], From = a, To = b, Path = [a,b]   ;
Ess = [a-[b],b-[c,a]], From = a, To = c, Path = [a,b,c] ;
false.

Редактировать 2015-06-02

Еще один выстрел в лучшее название! Это больше зависит от стороны maplist/2...

graph_path_from_to(P_2,Path,From,To) :-
   path(P_2,Path,From,To).

Здесь graph, конечно, является существительным, а не глаголом.

Что касается значения "путь": пути определенно должны допускать From=To и не исключать этого по умолчанию (с попарно-временными неравенствами). Это легко исключить с помощью дополнительной цели dif(From,To), но не наоборот.

Ответ 2

Как насчет определения path/4 как это?

path(R_2, Xs, A,Z) :-                   % A path `Xs` from `A` to `Z` is ...
   walk(R_2, Xs, A,Z),                  % ... a walk `Xs` from `A` to `Z` ...
   all_dif(Xs).                         % ... with no duplicates in `Xs`.

Чтобы помочь универсальному завершению, мы заменяем две цели в соединении выше...

path(R_2, Xs, A,Z) :-
   all_dif(Xs),                         % enforce disequality ASAP
   walk(R_2, Xs, A,Z).

... и используйте следующую ленивую реализацию all_dif/1:

<Предварительно > all_dif (Xs): -% принудительно использовать неравенство с парой термов  freeze (Xs, all_dif_aux (Xs, [])). % (может быть отложено) all_dif_aux ([], _). all_dif_aux ([E | Es], Vs): -  maplist (dif (E), Vs),% никогда не задерживается  freeze (Es, all_dif_aux (Es, [E | Vs])). % (может быть отложено)

walk/4 определяется как path/4 и path/5, заданный OP:

:- meta_predicate walk(2, ?, ?, ?).
walk(R_2, [X0|Xs], X0,X) :-
   walk_from_to_step(Xs, X0,X, R_2).

:- meta_predicate walk_from_to_step(?, ?, ?, 2).
walk_from_to_step([], X,X, _).
walk_from_to_step([X1|Xs], X0,X, R_2) :-
   call(R_2, X0,X1),
   walk_from_to_step(Xs, X1,X, R_2).

IMO выше path/4 проще и доступнее, особенно для новичков. Согласитесь?

Ответ 3

Я не вижу причины определять в пути /4 аргументы "start node" и "end node". Кажется, что достаточно простого пути /2 с правилом и списком узлов.

Если пользователю нужен список, начинающийся с некоторого node (например, 'a'), он может запросить оператор как: path (some_rule, ['a' | Q]).

Пользователь может, например, запросить путь, длина которого равна 10: length (P, 10), path (some_rule, P).

* Добавление 1 *

Некоторые цели полезности могут быть легко добавлены, но они не являются основным предметом. Например, путь /3 с началом node равен:

path( some_rule, [start|Q], start ) :- 
  path ( some_rule, [start|Q ] ).   

* Добавление 2 *

Добавление последнего node в качестве аргумента может дать ложную идею о том, что этот аргумент управляет алгоритмом, но это не так. Предположим, например:

n(a, b).
n(a, c).
n(a, d).

и выполнение алгоритма трассировки для запроса:

[trace]  ?- path( n, P, X, d ).
   Call: (6) path(n, _G1025, _G1026, d) ? creep
   Call: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Exit: (7) path(n, [], d, d, [d]) ? creep
   Exit: (6) path(n, [d], d, d) ? creep
P = [d],
X = d ;
   Redo: (7) path(n, _G1107, _G1026, d, [_G1026]) ? creep
   Call: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, b) ? creep

   Call: (8) non_member(b, [a]) ? creep
   Call: (9) dif:dif(b, a) ? creep
   Exit: (9) dif:dif(b, a) ? creep
   Call: (9) non_member(b, []) ? creep
   Exit: (9) non_member(b, []) ? creep
   Exit: (8) non_member(b, [a]) ? creep
   Call: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Call: (9) n(b, _G1118) ? creep
   Fail: (9) n(b, _G1118) ? creep
   Fail: (8) path(n, _G1113, b, d, [b, a]) ? creep
   Redo: (9) non_member(b, []) ? creep
   Fail: (9) non_member(b, []) ? creep
   Fail: (8) non_member(b, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, c) ? creep

   Call: (8) non_member(c, [a]) ? creep
   Call: (9) dif:dif(c, a) ? creep
   Exit: (9) dif:dif(c, a) ? creep
   Call: (9) non_member(c, []) ? creep
   Exit: (9) non_member(c, []) ? creep
   Exit: (8) non_member(c, [a]) ? creep
   Call: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Call: (9) n(c, _G1118) ? creep
   Fail: (9) n(c, _G1118) ? creep
   Fail: (8) path(n, _G1113, c, d, [c, a]) ? creep
   Redo: (9) non_member(c, []) ? creep
   Fail: (9) non_member(c, []) ? creep
   Fail: (8) non_member(c, [a]) ? creep
   Redo: (8) n(_G1026, _G1112) ? creep

   Exit: (8) n(a, d) ? creep

   Call: (8) non_member(d, [a]) ? creep
   Call: (9) dif:dif(d, a) ? creep
   Exit: (9) dif:dif(d, a) ? creep
   Call: (9) non_member(d, []) ? creep
   Exit: (9) non_member(d, []) ? creep
   Exit: (8) non_member(d, [a]) ? creep
   Call: (8) path(n, _G1113, d, d, [d, a]) ? creep
   Exit: (8) path(n, [], d, d, [d, a]) ? creep
   Exit: (7) path(n, [d], a, d, [a]) ? creep
   Exit: (6) path(n, [a, d], a, d) ? creep
P = [a, d],
X = a .

как вы можете видеть, в этом случае алгоритм не может использовать команду перебора. По этой причине, если алгоритм не улучшен, я предлагаю не добавлять "end node" в качестве аргумента "path".