1

I learn Prolog at university and keep stumbling on someting rather odd during the home excercises. I wrote following Prolog clauses, which are part of a much bigger program:

edges(X,Edges):-
    findall(Edge,(highway(X,Y,Edge);highway(Y,X,Edge)),Edges).

edgesList([],_).
edgesList([node(X)|InL],OutL):-
    member((node(X),Edges),OutL),
    edges(X,Edges),
    edgesList(InL,OutL).

Which use following facts:

highway(1,2,yellow). 
highway(2,3,blue). 
highway(1,3,yellow).

You can see highway as a fact that describes two nodes in the first two arguments and an edge in the third. All facts together form a connected graph.

With the clause edgesList, I want to list the edges per node e.g.

Result = [(node(1),[yellow,yellow]),(node(2),[blue,yellow]),(node(3),[blue,yellow])]

But when I write my query:

edgesList([node(1),node(2),node(3)],List).

I get following result:

List = [(node(1),[yellow, yellow]), (node(2),[blue, yellow]), (node(3),[blue, yellow])|_G610]

For some reason, Prolog won't unify the tail of the result-list with the empty list, despite the fact that the member-predicate is used correct, I assume. It's something that happend a few times now in different excercises and it would be good to know what I did wrong...

Programmer1994
  • 955
  • 3
  • 13
  • 29
  • There are essentially an infinite number of lists for which `member((node(X),Edges),OutL)` is true if `OutL` is a variable. At a Prolog prompt, see what happens if you do `member(a, L).` and press `;` (see the next answer) after each result. Also, your base case `edgesList([],_).` is incorrect. `_` doesn't mean *nothing*, but it means *anything* since `_` is an anonymous variable. What does the edges list for `[]` look like? Surely it's not `_` (anything). – lurker Aug 07 '16 at 11:34

1 Answers1

1

The problem is in the clause:

edgesList([],_).

because in the end it will fill the list with an uninstantiated tail (|_G610).

One solution is :

edges(X,Edges):-
    findall(Edge,(highway(X,Y,Edge);highway(Y,X,Edge)),Edges).

edgesList([],[]).
edgesList([node(X)|InL],[(node(X),Edges)|T]):-
   edges(X,Edges),
   edgesList(InL,T).
coder
  • 12,832
  • 5
  • 39
  • 53
  • So I can't use member at all in this case? Why can't I combine member with a base-case with two empty lists? – Programmer1994 Aug 06 '16 at 22:37
  • I don't think the problem is in using member in generally, but if you use member and base-case the two empty lists then ,think that it will work fine and when it comes to base case you pass the OutL List and demand to be empty which can't be so this isn't going to work. – coder Aug 06 '16 at 22:49
  • Well, still don't understand the "when to use member, and when to just append the head" quit wel, but your answer worked fine, thanks! :) – Programmer1994 Aug 07 '16 at 10:49
  • @CedricCornelis: Almost every time you need to build a list (like this example) the easiest and more clear way is by "pattern matching" to define the head and do it recursively for the tail .Using member is not wrong but you need to make base case clear so the list will be well defined ,which is I think much more difficult. For example by using member a solution would be: edgesList(InL,OutL):-edgesList(InL,OutL,0). edgesList([],L,N):-length(L,N). edgesList([node(X)|InL],OutL,N):-member((node(X),Edges),OutL),edges(X,Edges), N1 is N+1,edgesList(InL,OutL,N1),!. – coder Aug 07 '16 at 12:56