Reputation: 157
I need to define a predicate eats/3 (Eater, Food, Time) which checks whether the Eater will eat the Food, depending on what Time it is.
I have the following semantic framework describing the biological relations between its components:
is_a(animal, living_being).
is_a(plant, living_being).
is_a(herbivore, animal).
is_a(carnivore, animal).
is_a(omnivore, animal).
is_a(flowering_plant, plant).
is_a(nonflowering_plant, plant).
is_a(fox, carnivore).
is_a(cow, herbivore).
is_a(hen, omnivore).
is_a(bessie, cow).
is_a(hawke, fox).
is_a(coco, hen).
is_a(cabbage, nonflowering_plant).
is_a(rose, flowering_plant).
I also have the eats/2 predicate:
eats(carnivore, animal).
eats(herbivore, plant).
eats(omnivore, animal).
eats(omnivore, plant).
There are predicates animal_food/1 and plant_food/1:
animal_food(animal).
plant_food(plant).
The condition is that animal_food will only be eaten between the Time-s 22:00-06.00 and plant_food will only be eaten between the Time-s 06:01-21:59.
For example:
?- eats(fox, hen, 02.00).
will return true if fox is a carnivore or omnivore, hen is animal_food and carnivores/omnivores eat at night based on animal_food being eaten at night.
Another example:
?- eats(bessie, cabbage, 14.00).
will return true if bessie is a herbivore, cabbage is plant_food and herbivores eat during the day based on plant_food being eaten during the day.
Also, the eater cannot be of the same class as the Food (for example, foxes cannot eat foxes).
What I have made so far is this:
subclass(Who,Whose):-
is_a(Who,Whose).
subclass(Who,Whose):-
is_a(Who,Intermediary),
subclass(Intermediary,Whose).
This predicate subclass/2 will check whether or not it will eventually find a relation between the two classes.
With this I made a predicate eats/2:
eats(Who,Whom):-subclass(Who, Eater), subclass(Whom,Food), eats(Eater,Food), Who \= Food.
I have also made a predicate animal_food/1 and plant_food/1 so I could check if, for example a hen was of the type animal_food and a cabbage was of the type plant_food:
animal_food(Who):-subclass(Who,Whose), animal_food(Whose), Who \= Whose.
plant_food(Who):-subclass(Who,Whose), plant_food(Whose), Who \= Whose.
With all this I can for example get true if I ask
eats(fox,hen)
or
eats(cow,cabbage)
but where I am having an issue is 1) the Time condition of eats/3.
I have sort of an idea in pseudocode for checking the Time condition:
21.59 -> 21*60 + 59 = 1298. (minutes)
06.01 -> 6*60 + 1 = 361. (minutes)
(Time >= 361, Time=< 1298) -> daytime
Otherwise -> nighttime
but I am not quite sure how to parse something like 15.30 so that I can make the check. Make it a list and then do the multiplication on the numbers separately?
and
2) constructing the predicate so that it would check all the conditions (Eater being carnivore/omnivore/herbivore, Food being plant_food or animal_food and Time fitting with Food). I have made the simple checks that check for empty variables
eats([],Food,Time):- fail.
eats(Eater,[],Time):- fail.
eats(Eater,Food,[]):- fail.
I assume the main check would be, for example, for a fox:
% this is pseudocode
eats(fox,hen,05.59):- Term1 =.. [subclass,fox,X], Term1, (animal_food(hen);plant_food(hen)), Number is 5*60 + 59, (Number>= 361, Number=< 1298) % if the Food is plant food; (!(Number>= 361, Number=< 1298)) %if the Food is animal_food
Any help will be appreciated.
Upvotes: 2
Views: 257
Reputation: 157
Right, many thanks to @daniel-lyons for the help. I had some conflicts regarding the names of the predicates with the facts. I ended up replacing eats/2 with eats_rule/2 and plant_food/1 plus animal_food/1 with plant_food_rule/1 plus animal_food_rule/1. The only thing I changed was the name of the predicates.
The answer:
eats(Who,Whom):-subclass(Who, Eater), subclass(Whom,Food), eats(Eater,Food), Who \= Food.
time_to_minutes(TimeAsNumber, TimeAsMinutes):-
Hours is floor(TimeAsNumber),
Minutes is integer(100*(TimeAsNumber-Hours)),
between(0,23,Hours), between(0,59,Minutes),
TimeAsMinutes is ((Hours *60) + Minutes).
daytime(Time):- time_to_minutes(Time,TimeResult), (TimeResult>=361, TimeResult=< 1319) .
nighttime(Time) :- time_to_minutes(Time,TimeResult), ((TimeResult>= 1320 , TimeResult=< 1439) ;
(TimeResult>= 0, TimeResult=< 360)).
eats(Who,Whom,Time):- eats_rule(Who,Whom), animal_food_rule(Whom), nighttime(Time).
eats(Who,Whom,Time):- eats_rule(Who,Whom), plant_food_rule(Whom), daytime(Time).
Upvotes: 1
Reputation: 22803
but I am not quite sure how to parse something like 15.30 so that I can make the check. Make it a list and then do the multiplication on the numbers separately?
Let's review your condition:
The condition is that animal_food will only be eaten between the Time-s 22:00-06.00 and plant_food will only be eaten between the Time-s 06:01-21:59.
I probably wouldn't encode time as a decimal number, but let's say you did; I think the predicate would work fine if you expressed it in the natural way:
daytime(Num) :- 6.01 =< Num, Num =< 21.59.
nighttime(Num) :- (22 =< Num, Num =< 24); (0 =< Num, Num =< 6).
This works; the problem is that it also works for crazy times (6.79) and doing arithmetic on time in this representation is not going to work properly. But, to be honest, every representation has shortcomings and this one is actually probably sufficient for your problem as described, so I would probably tolerate it. But I'm pretty tolerant.
In Prolog, you get the benefit that you can use standard operators or define new operators if it suits your problem. Colons tend to be problematic, but it seemed to work for me to do something like this:
daytime(Time) :- 6:01 @=< Time, Time @=< 21:59.
nighttime(Time) :- (22:00 @=< Time, Time @=< 24:00) ; (0 @=< Time, Time @=< 6:00).
The atom comparison there continues to allow for invalid times, but there's no particular reason to address it in the problem description you gave.
constructing the predicate so that it would check all the conditions (Eater being carnivore/omnivore/herbivore, Food being plant_food or animal_food and Time fitting with Food).
I would remove your fail cases; Prolog usually doesn't need any help figuring out that things fail when they are incomplete. You can instead just use things assuming they are there and if they aren't, it will fail.
You have a natural language definition of what you want it to do, so start there:
eats(Eater, Food, Time) :-
eats(Eater, Food),
animal_food(Food),
nighttime(Time).
eats(Eater, Food, Time) :-
eats(Eater, Food),
plant_food(Food),
daytime(Time).
I think you actually have all the other parts you need. Does this work?
As far as I can see, your task doesn't actually rely on any arithmetic or conversions with time. So all we have to do to worry about valid time is test for it, and make sure that test is in play.
valid(HH:MM) :- between(0, 24, HH), between(0, 60, MM).
This predicate affirms the validity of a time value. If an invalid time value is passed in, it will fail. Try it. Now let's plug it into our other time predicates:
daytime(Time) :-
valid(Time), 6:01 @=< Time, Time @=< 21:59.
nighttime(Time) :-
valid(Time),
((22:00 @=< Time, Time @=< 24:00) ;
(0 @=< Time, Time @=< 6:00)).
That should be the only change you need to make.
Upvotes: 3