Definition of a path/trail/walk

后端 未结 3 1181
你的背包
你的背包 2020-11-22 04:21

Many predicates define some kind of an acyclic path built from edges defined via a binary relation, quite similarly to defining transitive closure. A generic definition is t

相关标签:
3条回答
  • 2020-11-22 04:59

    How about defining path/4 like this?

    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`.
    

    To aid universal termination, we swap the two goals in above conjunction ...

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

    ... and use the following lazy implementation of all_dif/1:

    all_dif(Xs) :-                          % enforce pairwise term inequality
       freeze(Xs, all_dif_aux(Xs,[])).      % (may be delayed)
    
    all_dif_aux([], _).
    all_dif_aux([E|Es], Vs) :-               
       maplist(dif(E), Vs),                 % is never delayed
       freeze(Es, all_dif_aux(Es,[E|Vs])).  % (may be delayed)
    

    walk/4 is defined like path/4 and path/5 given by the 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 above path/4 is simpler and more approachable, particularly for novices. Would you concur?

    0 讨论(0)
  • 2020-11-22 05:03

    I want to focus on naming the predicate.

    • Unlike maplist/2, the argument order isn't of primary importance here.

    • The predicate name should make the meaning of the respective arguments clear.

    So far, I like path_from_to_edges best, but it has its pros and cons, too.

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

    Let's pick it apart:

    • pro: path is a noun, it cannot be mis-read a verb. To me, a list of vertices is implied.

    • pro: from stands for a vertex, and so does to.

    • con: edges is somewhat vague, but using lambdas here is the most versatile choice.

    • con: According to Wikipedia, a path is a trail in which all vertices (except possibly the first and last) are distinct. So that would need to be clarified in the description.


    Using lambdas for a lists of neighbor vertices 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.
    

    Edit 2015-06-02

    Another shot at better naming! This leans more on the side of maplist/2...

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

    Here, graph, of course, is a noun, not a verb.

    Regarding the meaning of "path": paths definitely should allow From=To and not exclude that by default (with pairwise term inequalities). It is easy to exclude this with an additional dif(From,To) goal, but not the other way round.

    0 讨论(0)
  • 2020-11-22 05:05

    I do not see the reason to define in path/4 the arguments "start node" and "end node". It seems that a simple path/2 with the rule and the list of nodes must be enough.

    If the user wants a list starting with some node (by example, 'a'), he can query the statement as: path( some_rule, ['a'|Q] ).

    A user could, by example, request for path that have length 10 in the way: length(P,10), path( some_rule, P).

    * Addendum 1 *

    Some utility goals can be easily added, but they are not the main subject. Example, path/3 with start node is:

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

    * Addendum 2 *

    Addition of last node as argument could give the false idea that this argument drives the algorithm, but it doesn't. Assume by example:

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

    and trace algorithm execution for the query:

    [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 .
    

    as you can see, in this case algorithm fails to brute force. For this reason, if algorithm is not improved, I suggest do not add "end node" as "path" argument.

    0 讨论(0)
提交回复
热议问题