rnso
rnso

Reputation: 24535

Input list not obtained in Prolog

I am using following code:

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

main:-
    getItems(L),
    writeln('-----------1-------------'),
    writeln(L),
    findall((A,B,C), item(A,B,C), M),
    writeln('-----------2-------------'),
    compareItem(M,L,I),
    writeln('-----------3-------------'),
    writeln(I).

getItems([Item|List]):-
    writeln('Enter:'),
    read(Item),
    dif(Item,stop),
    getItems(InputList).

getItems([]).

compareItem([H|T],L,Out):-
    writeln('-----------4-------------'),
    intersection(H,L,I),
    writeln('-----------5-------------'),
    compareItem(T,L,[I|Out]).
compareItem([],L,Out).

It gives following output:

?- main.
Enter:
|: a.
Enter:
|: b.
Enter:
|: stop.
-----------1-------------
[a|_G1003]
-----------2-------------
-----------4-------------
-----------1-------------
[a|_G1003]
-----------2-------------
-----------4-------------
-----------1-------------
[]
-----------2-------------
-----------4-------------
false.

4 ?-

The input method has been taken from: Creating a list from user input with swi-prolog

I get following when commands are run separately:

?- findall((A,B,C), item(A,B,C), M)
M = [ (a, b, c), (a, a, c), (a, d, c), (a, c, b)].
?- intersection([a,b,c],[a,b],I).
I = [a, b].

However, it is [(a,b,c),...] in M and I expected user input to be [a,b] (here it is blank).

I want the output to be the intersection of user input with each of items in M:

[(a,b), (a,a), (a), (a,b)]

Also, after reaching point 4, how does the execution reach again point 1?

How can these be corrected? Thanks.

Upvotes: 0

Views: 272

Answers (2)

lurker
lurker

Reputation: 58244

(a,b,c) is not a list in Prolog. It's a term which represents the canonical form: ','(a,','(b,c)). A list [a,b,c] represents the canonical form, '.'(a,'.'(b,'.'(c,[]))). (a,b,c) will simply not behave like a list so will not give you the results you are seeking.

If you want to handle your collection as lists, then use findall([A,B,C], item(A,B,C), M).

compareItem([H|T],L,[I|Out]):-
    writeln('-----------4-------------'),
    intersection(H,L,I),
    writeln('-----------5-------------'),
    compareItem(T,L,Out).
compareItem([],_,[]).

Two other issues with the original predicate which are fixed above are:

  1. The base case needs to indicate that the empty list [] is the result of comapring with the empty list. Otherwise, Out is never instantiated.
  2. You have misplaced the I variable in the recursion, as it needs to be the head of the 3rd argument in the result, not in the recursive call.

You can, in fact, use maplist for this task:

compareItem(M, L, Out) :-
    maplist(intersection(L), M, Out).

This will be missing your writeln calls. You can add one back if you wish:

intersect_with_writeln(A, B, R) :-
    writeln('-----------4-----------'),
    intersect(A, B, R).

compareItem(M, L, Out) :-
    maplist(intersect_with_writeln(L), M, Out).

Upvotes: 1

Fatalize
Fatalize

Reputation: 3543

You only get the first element because you never use the list that contains the rest:

getItems([Item|List]):-
    writeln('Enter:'),
    read(Item),
    dif(Item,stop),
    getItems(InputList).

See the too bolded variables? They have different names but they need to have the same one, otherwise you're just discarding InputList and "returning" a free List from getItems/1.

You could have seen that mistake when compiling your program: your Prolog distribution probably said something along the line of:

Singleton variables: [List,InputList]

Which means that both those variables are only used once in a rule, and is therefore probably a mistake.

Upvotes: 1

Related Questions