1

I'm a beginner in Prolog and I am dealing with a problem that might seem stupid to you, but I really can't understand what I'm doing wrong! Ok, I have this file fruits.pl and inside that I have something like this:

fruit(apple,small,sweet).
fruit(lemon,small,nosweet).
fruit(melon,big,sweet).

I have already (inside that file made a coexist(X,Y) atom that checks if two fruits can be put together in a plate. It works fine! But now I can't create a suggest(X) that takes as a parameter a fruit and returns a list of fruits that can be put together in the same plate. The thing is I was trying to make something like that

suggest(X) :- findall(Y,fruit(Y,_,_), List), coexist(X,Y).

What do you think? Every time I try to run this in swi prolog there is a warning 'singleton variable' and when I press

suggest(apple).

then it says false.. sorry for my english :/

elli
  • 1,109
  • 1
  • 13
  • 20

2 Answers2

2

Predicates in Prolog do not return anything. You have goals that are satisfied or not and you can interpret that as returning true or false.

Your predicate suggest(X) should contain another parameter that will be bound to the list of fruits that go together with X. An option would be: suggest(X, List) which describes the following relation: List represents all the fruits that go together with X. Then, you could ask:

?- suggest(apple, List).
List = [pear, cherry].

The goal findall(Y, ... , ...) uses the Y variable internally and Y is still unbound after the goal is satisfied. So, you should move coexist(X,Y) inside the second argument of findall/3 which is the goal that is satisfied in all possible ways. Th rule below works only if X is instantiated (suggest(+X, -List)).

suggest(X, List) :- findall(Y, (fruit(Y,_,_), coexist(X, Y)), List).

You can read this as follows: "List represents all fruits Y that coexist with X".

Tudor Berariu
  • 4,910
  • 2
  • 18
  • 29
  • What if I want to limit the number of fruits, depending on which one is big and which one is small? I mean, in suggest I want maximum 2 fruits in the plate if the fruit I have chosen is small and maximun 3 if the fruit is big. – elli Jun 18 '14 at 19:39
  • To limit the length of `List` you can add the following two goals to the rule: `between(1, 3, Len), length(List, Len)`. – Tudor Berariu Jun 18 '14 at 19:43
  • Add this facts: `limit(small,2).` and `limit(big,3).` and the rule `suggest(X, List) :- findall(Y, (fruit(Y,_,_), coexist(X, Y)), List), X = fruit(_, Size, _), limit(Size, N), between(0, N, Len), length(List, Len).` – Tudor Berariu Jun 18 '14 at 19:45
  • @eLsA: you are now completely changing what you want. Use a new question should that be important. – false Jun 18 '14 at 20:00
  • Sorry, did a mistake: `X = fruit(_, Size, _)` instead of `fruit(X, Size, _)`. – Tudor Berariu Jun 18 '14 at 20:20
  • Sorry, again it says false ... :/ – elli Jun 18 '14 at 22:10
  • could I please, somehow, send you a private msg with my code so you could tell me what I am doing wrong? Of course, I'll understand if you say no, you have already helped me a lot! – elli Jun 18 '14 at 22:20
  • You must have 20 reputation on Stack Overflow to talk here. And I only have 6! Can I give you a mail I use or something like that? – elli Jun 19 '14 at 11:32
  • elsa_linosw@yahoo.com please Its very important :/ – elli Jun 19 '14 at 11:40
2

When you try to define a predicate in Prolog, first of all pretend that you have written that predicate already and start with imagining how you would use it. That is, what queries you would like to pose.

To me, it looks as if coexist/2 already describes what you want. BTW, may_coexist/2 might be a more descriptive name. Why do you want this in a separate list? And why using fruit/3 at all? But for the sake of the question let's assume that this makes sense. So essentially you would have now a relation fruit_compatible/2:

fruit_compatible(F, G) :-
   fruit(F, _, _),
   may_coexist(F, G),
   fruit(G, _, _).  % maybe you want to add this?

And now, let's assume you want this list too. So you would have a relation fruit_suggestions/2. How to use it?

?- fruit_suggestions(apple, L).
   L = [cherry,pear].

or ... should it be rather L = [pear,cherry]? Or both?

?- fruit_suggestions(lemon, L).
   L = [orange].

So every time I want a suggestion I have to think of a fruit. Always thinking: what fruit should it be? Fortunately there is a less demanding way in Prolog: Simply use a variable instead of the fruit! Now we should get all suggestions at once!

?- fruit_suggestions(F, L).
   F = apple, L = [cherry, pear]
;  F = lemon, L = [orange]
;  F = cromulon, L = [embiggy, mushfruit].

So we need to implement it such that it will behave that way. findall/3 alone does not solve this. And implementing it manually is far from trivial. But there is setof/3 which handles variables in exactly that manner. Many of the tiny nitty-gritty design decisions have already been made, like that the list will be sorted ascendingly.

fruit_suggestions(F, L) :-
   setof(G, fruit_compatible(F, G), L).

Edit: Due to the discussion below, here would be a solution that also permits empty lists. Note that this sounds trivial but it is not. To see this, consider the query:

?- fruit_suggestions(F, []).

What does it mean? What should F be? Also things that are no fruits at all? In that case we would have to produce solutions for everything. Like F = badger ; F = 42 ; .... Most probably this does not make much sense. What might be intended is those fruits that are incompatible with everything. To this end, we need to add a new rule:

fruit_suggestions(F, []) :-
   setof(t,X^Y^fruit(F,X,Y),_),
   \+ fruit_compatible(F, _).
fruit_suggestions(F, L) :-
   setof(G, fruit_compatible(F, G), L).
false
  • 10,264
  • 13
  • 101
  • 209
  • There might be one problem with this solution: `setof/3` fails if the goal cannot be satisfied. So if there are no fruits that are compatible with a given one `F`, the result will be `false` instead of succeeding with `L=[]`. Of course, it's easy to add a cut operator and a second rule `fruit_suggestions(_,[]).` – Tudor Berariu Jun 18 '14 at 19:07
  • We should use instatiation patterns for arguments when describing predicates. Your solution is the best for `suggest(?Fruit,?List)`, but the `findall/3` might be better for `suggest(+Fruit,-List)`. – Tudor Berariu Jun 18 '14 at 19:09
  • @Tudorberiariu: How do you control the modes/instantiation patterns you are suggesting? – false Jun 18 '14 at 19:10
  • 1. I don't know if it should. It depends on what who ever uses it wants. It might. 2. By adding them in our questions and answers and asking those who post question to use them. – Tudor Berariu Jun 18 '14 at 19:32
  • 1
    The question `[]` or not is separate from the question of being consistent or not. In your solution `suggest(X, List)` gives a very long list. That must be prevented: Either by an error or by a better implementation. – false Jun 18 '14 at 19:35
  • Your point is correct, but I can interpret the relation `suggest/2` in such a way that a long list with a lot of duplicates is the correct answer for `suggest(_, List)`. – Tudor Berariu Jun 18 '14 at 19:41
  • 1
    @TudorBerariu: In that case, `suggest/2` is not a relation. That's the point. – false Jun 18 '14 at 19:44