0

I've been learning and I came across a problem. I understood the cut operator from the tutorials but I'm trying to solve a problem and I cannot understand the solution.

Problem:

If the car color is red, made in Italy, then it's a Ferrari. If it's red but made in Germany (or any other country. could be more than one), it's Benz. If it's not red and is big, it's ford. If it's not red and not big, it's Toyota.

That is:

red & Italy: Ferrari
red & Germany (or not Italy): Benz
not red & big: ford
not red & not big: Toyota

Given some facts for a particular car object:

color(cx, red).
speed(cx, 220).
make(cx, italy).
type(cx, sport).

I want to write a predicate brand(X, name) that will return the brand of the particular car objects, like:

brand(X, ferrari):-
   color(X,red), make(X,T), T=italy.
brand(X, benz) :-
   color(X,red), not(make(X,italy)).
brand(X, ford) :-
   not(color(X,red)), size(X,big).
brand(X, toyota) :-
   not(color(X,red)), not(size(X,big)).

Question is how (and where) do I use the cut operator here so that it doesn't check the same property (eg: here "make") twice? I can't seem to wrap my head around this.

If I check for red and then for make, if the make turns out not italy, how do I write the brand(X, brand_name) for a set of facts for car object "ck" such that it doesn't check the make again? It appears impossible to me.

false
  • 10,264
  • 13
  • 101
  • 209
user2559578
  • 143
  • 1
  • 10
  • I think `brand(X, ferrari):- color(X,red),!, make(X,T), T=italy.` can be read as "the only way a red car can be ferrari is when it is made in italy. " Hope that helps I'm no expert on prolog. – Adder Apr 18 '17 at 13:54
  • See [this answer](http://stackoverflow.com/a/14566333/1812457) in particular. –  Apr 18 '17 at 18:41

1 Answers1

3

The short answer: Don't.

Almost invariably, using !/0 will lead to loss of valid solutions.

Always keep in mind that Prolog programs are typically much more general than programs in other languages. In particular, keep in mind that users can always post the most general query, which in your case is:

?- brand(X, Y).

If you have !/0 in this definition, then yes, you will likely "check only once", but on the other hand, you will fail to generate all valid answers.

One good way out is to use if_/3 from library(reif). For example, in your case, you can write the facts as:

color(cx, red, true).
speed(cx, 220, true).
make(cx, italy, true).
type(cx, sport, true).

And now you can write:

car_type(C, Type) :-
        if_(color(C,red),
            if_(make(C, italy), Type=ferrari, Type=bentz),
            if_(type(C, big), Type=ford, Type=toyota)).

Importantly, the most general query will still work even if there are multiple solutions:

?- car_type(C, Type).
C = cx,
Type = ferrari.
Community
  • 1
  • 1
mat
  • 40,498
  • 3
  • 51
  • 78
  • Thanks for the answer but I'm trying to learn how to use cut and in my specific example, I can't change the facts to include "true". Let's say I read a bunch of facts from a file and there are no general queries like brand(X,Y). The assumption is the query will always be of the form brand(cx, name) where cx is an object whose facts are provided. I want the predicate `brand(X, name)` to return the brand name of `cx` as benz, ferrari, etc. – user2559578 Apr 18 '17 at 14:19
  • Use `once/1` to commit to the first solution **when querying**, but do not mess up your whole program: `?- once(brand(cx, name)).` It is good practice to retain the generality of your relations such that they *can* be queried in all directions if needed. – mat Apr 18 '17 at 14:23
  • 1
    I don't think we're on the same page. I'm learning the topic of cuts (and negation). I want to know the usage and side effects (pitfalls) of cut in this code. I do not want a perfect solution. – user2559578 Apr 18 '17 at 14:29
  • Please read the existing answers and others that are shown in "Linked" and "Related" in the menu on the right of this page. Using `once/1` solves your immediate issue. If you have any further questions, please file a new question, and I will answer it there. Specifically, if you want to know more about possible pitfalls and side-effects of `!/0`, please file a question that *asks about them*, since you did not mention any of this in the present question. In general, `!/0` is not a good approach to solve your issue. Look into if-then-else, and then, ideally, generalize this to use `if_/3`. – mat Apr 18 '17 at 14:40
  • You need to define also the cases for `false`! – false Apr 18 '17 at 19:48